w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.46k stars 657 forks source link

[css-logical] Flow-relative syntax for `margin`-like shorthands #1282

Open fantasai opened 7 years ago

fantasai commented 7 years ago

CSS currently assigns the values in the margin/padding/border shorthand to their physical longhands, i.e.

  margin: 1em 2em 3em 4em;

is equivalent to

  margin-top: 1em;
  margin-right: 2em;
  margin-bottom: 3em;
  margin-left: 4em;

I'm pretty sure we want some equivalent syntax for assigning into the logical longhands instead, but what should that be?

The current proposal is to put a keyword in front of the 4 values, like

  margin: relative 1em 2em 3em 4em;

Another possibility is to use a !keyword:

  margin: 1em 2em 3em 4em !relative;

or to create a new property in its place:

  margin-relative: 1em 2em 3em 4em;

or use some entirely as-yet-unused symbol or syntax.

And of course the exact keyword, if one is used, is up for debate as well; shorter would be better. People will be drawing up entire style sheets that use almost exclusively flow-relative properties, so this needs to be designed for comfort under frequent use.

Ideas welcome~

chharvey commented 7 years ago

I agree with the current proposal, a keyword in front of the values. I personally like relative better than logical, because I don't think this spec really has anything to do with "Logic" per se… (the study of truth and argument forms).

A couple of points:

  1. the !relative keyword isn't already part of the standard CSS syntax and doesn't (and shouldn't) apply to all properties like the !important keyword does.
  2. adding a new property like margin-relative would have to set (or reset) its sub-properties, but sub-property names are usually extensions of the shorthand. So a margin-relative shorthand would imply its sub-properties are things like margin-relative-block-start, margin-relative-block-end, etc. There are exceptions, though, in the case of grid-gap setting grid-row-gap and grid-column-gap—property names that still make me mad!
MurakamiShinyu commented 7 years ago

margins for logical shorthand -- is this bad idea? I think plural word for shorthand makes sense, and columns is shorthand for column-* .

chharvey commented 7 years ago

@MurakamiShinyu — I see where you are going with this, but it could potentially become often confused with margin, which already exists. (Did they mean to put margins or was that a typo? Which one is relative again, I can't remember? etc etc…)

I think with a keyword such as relative prepended to the values, the code indicates very clearly and unambiguously what was intended—at the cost of just a few more bytes.

columns is okay because there is no column property, but in retrospect I would probably have preferred column for consistency.

MurakamiShinyu commented 7 years ago

I admit it's a crazy idea: 'margins' stands for "margin's writing-mode relative shorthand". But there is one advantage: 'margins' is easier to type than 'margin-relative' or 'margin: relative ...'. My concern is that people may feel troublesome to type a lot of 'relative' when css-logical-props becomes widely available.

bradkemper commented 7 years ago

I like the idea of !keyword that could be added to any 4-value clockwise shorthand (and their 3-value, 2-value, and 1-value versions). How about just an unadorned !, to make it an even easier, quicker switch? So...

border-radius: 2em 1em 4em / 0.5em 3em !;

is equivalent to

border-block-start-inline-start-radius:  2em 0.5em;
border-block-start-inline-end-radius:    1em 3em;
border-block-end-inline-end-radius:      4em 0.5em;
border-block-end-inline-start-radius:    1em 3em;

Adding the ! to a 1-value version, like margin: 0 ! wouldn't have any noticeable effect, but should be allowed.

cork commented 7 years ago

Why not go the same route as for box-sizing? So something like margin-mode: relative;

Then when the support gets common you could just do:

* { margin-mode: relative; }

div {
  margin: 1em 2em 3em 4em;
}

#exception {
  margin-mode: physical;
  margin: 1em 2em 3em 4em;
}
SebastianZ commented 7 years ago

Having a margin-mode property would go hand in hand with the current proposal of margin: relative 1em 2em 3em 4em;, i.e. be another longhand property for margin. The disadvantage of this is that its value infuences the handling of the other values, which might be unexpected.

I totally agree with @chharvey's comment that !relative (or ! for the same reason) and margin-relative have downsides.

Sebastian

inoas commented 7 years ago

If I got the ticket right (because the opening post doesn't really explain):

I think I have already answered via twitter... but I am suggesting transpose again. And from an author point of view I would not care it it was margin-transpose: val val val val or margin: val val val val transpose;

chharvey commented 7 years ago

@cork I do like your idea of margin-mode. That way the syntax of the margin property would not have to change. The syntax of margin-mode would be

syntax:    [ physical | relative ] | inherit | initial | unset

initial:   physical

inherited: true

One upside would be that you wouldn't have to change the margin property when you want to change the mode—taking advantage of the Cascade, resulting in more readable diff.

Alas, a recent change of the spec indicates a different order when relative values are used.

p {
  margin: 1px 2px 3px 4px;
  /* equivalent to standard order (top, right, bottom, left)
  margin-top:    1px;
  margin-right:  2px;
  margin-bottom: 3px;
  margin-left:   4px; */
}
p.relative {
  margin-mode: relative; /* <-- added */
  /* equivalent to new order:
  margin-block-start:  1px; `top`    in LTR-TB
  margin-inline-start: 2px; `left`   in LTR-TB
  margin-block-end:    3px; `bottom` in LTR-TB
  margin-inline-end:   4px; `right`  in LTR-TB */
}

So in the example above, by adding margin-mode: relative;, you would still have to add margin: 1px 4px 2px 3px; in the second ruleset to keep things the same.

Another downside: you would have to have corresponding "mode" properties for the following, which could be a bit much:

MatsPalmgren commented 7 years ago

I suspect margin-mode might be hard to implement - it's easier if you know at parse time how a property is supposed to be interpreted.

I like the !relative or ! proposal best so far. It would make it possible for multi-value properties to have both physical/logical values in the same declaration (if we want that). For example: background-position: 10px, ! 20px, 30px, ! 40px;

I'm concerned though, that this might lead to new CSS properties being defined as physical-first and require ! for logical values. This would be unfortunate since logical values are superior in most cases. Perhaps ! could be interpreted as a general physical/logical switch though, so that for example grid-gap: ! 10px 20px; would set 10px on the longhand that corresponds to the vertical axis?

MurakamiShinyu commented 7 years ago

I like the idea of #1279: margin-block is shorthand for margin-block-start + margin-block-end, and margin-inline is shorthand for margin-inline-start + margin-inline-end. These will be often more useful than single margin flow-relative shorthand, and I am ok with adding 'relative' keyword when the margin shorthand is needed. Or how about the following syntax:

  margin: [ block <'margin-block'> || inline <'margin-inline'> ]

e.g.

  margin: block 1em 3em inline 2em 4em;
inoas commented 7 years ago

I have to raise my voice and say that short symbolic like a punctuation (!) mark are really not good practice IMHO. The gains in saved typing are minimal and the symbol does not transport its meaning. It pretty much feels regexpy and IMHO CSS should not be.

I'd favour extra logical properties. Aka margin and padding are absolute and margin-relative and padding-relative are not (dimensions for position and dimensions-relative or similar could be added later).

That is a very clear and easy interface for authors and there is no clash between old properties and new properties. Authors can also use margin and margin-relative independent of each other and thus are not breaking css parsing of older browsers!

So I really like @fantasai initial proposal.

fantasai commented 7 years ago

Wow, okay! Here's some responses:

MurakamiShinyu commented 7 years ago

Agree with @fantasai. Now I feel original relative keyword or -relative suffix were better. However, I think the word relative also has obscurity. People may think the relative is related to position: relative, and may not notice that it is about "flow-relative-directional". (For this reason I think logical keyword might be better then relative)

How about margin-bi (suffix -bi, stands for "block and inline")? I know this is exceptional in CSS property naming convention (avoid abbreviations and use complete words), but has the following advantages:

and CSS already has abbreviations in some keywords, e.g., "rl" (for "right to left direction") in vertical-rl value of writing-mode, and using "bi" for "block and inline directions" will be not too bad.

fantasai commented 7 years ago

Sorry; typo in commit message. :/

chharvey commented 7 years ago

If the keyword route is decided upon, can the syntax allow it to be at the beginning or end of the declaration? Inspired by box-shadow inset (that is, inset of the box-shadow, not the new inset positioning property).

So for margin it would be

relative? && [ <length> | <percentage> | auto ]{1,4}
bradkemper commented 7 years ago

Weird. My comment was posted to the wrong page

bradkemper commented 7 years ago

I really don't think we should have margin-* or *-margin as a property or !keyword for this. Because then you would need to do it for padding, border (and border-width, border-style, and border-color), border-radius, and many others. It should be a single !keyword that can be used for all (I agree that action-at-a-distance is bad for this).

If it is to be typed a lot (and really, that is the hope, that authors are considering bi-di and writing mode all the time), then it needs to be very short. I would say no more than 2-3 letters long. I still prefer an unadorned ! for that reason, even though it would more likely lead to authors adding it without understanding why.

jonjohnjohnson commented 6 years ago

I know there are multiple arguments against having a separate property that controls interpretations of shorthands for reasons like implementation difficulty or "action at a distance", but isn't that akin to how box-sizing works? I know there have been discussions about properties like box-size (https://github.com/w3c/csswg-drafts/issues/820), so wouldn't something like box-mode: [ physical | relative ] be what we'd all get behind if this was part of the initial proposal for box shorthands?

I'd imagine anyone who wants to use logical features would be "all in", not needing to set individual "modes" and wanting something like box-mode to cascade/inherit and set interpretation for ALL shorthand box properties such as border-width,padding,margin,border-radius, background-position, etc...

xfq commented 6 years ago

@fantasai wrote:

I agree with @inoas that using just ! alone is likely a bit too obscure (and also a bit too general, imho, as we use !keyword for other things like !important). However, I also agree with the concerns about !relative or relative or -relative potentially being too much of a typing burden; as I mentioned in the OP, we do expect this to become the default mode of assignment for many authors, so I'd say it's fair to trade a bit of obscurity for a bit of typing efficiency.

Even if we expect this to become the default mode of assignment for many authors, I would still prefer clarity & readability to typing efficiency, because the latter can be mitigated by code completion and code snippets in authoring tools, and IMHO shouldn't be a major concern (comparing with readability & less surprise for authors who are not familiar with flow-relative properties) when designing a language like CSS.

fantasai commented 6 years ago

I agree strongly with @bradkemper’s comment: this needs to be generic enough that it doesn't get confused with property-specific value spaces, and it also needs to be convenient enough that authors using flow-relative syntaxes are not at a significant ergonomic disadvantage compared to authors using physical syntaxes.

Another option would be to have a longer per-declaration !keyword (for clarity) but also a higher-level syntax similar to @namespace (changing the default interpretation of an entire stylesheet) or @media (changing the default interpretation in a block). This would be the most convenient, at the cost of making it possibly confusing if someone is copy-pasting style rules out-of-context.

fantasai commented 6 years ago

A third option would be to have some other not-currently-used single punctuation character somewhere in the declaration, to indicate flow-relative mapping, e.g.

margin: 1em 2em 3em 4em; /* physical mapping */
~margin: 1em 2em 3em 4em; /* logical mapping, option A */
margin ~: 1em 2em 3em 4em; /* logical mapping, option B */
margin :~ 1em 2em 3em 4em; /* logical mapping, option C */

This is convenient to type and safe for out-of-context quoting, at the cost of being more obscure.

chharvey commented 6 years ago

Ooh, I like @fantasai’s idea of an at-rule.

p { margin: 1em 2em 3em 4em; } /* physical mapping */

@mode (flow-mode: relative) {
  /* everything in here is logical mapping */
  p {
    margin: 1em 2em 3em 4em;
  }
  @mode (flow-mode: physical) {
    /* everything in here is physical mapping */
    blockquote { margin: 1em 2em 3em 4em; }
  }
}
jonjohnjohnson commented 6 years ago

I wouldn't be a huge fan of an at-rule solution when I'm already finding it cumbersome/unergonomic to get the most out of my stylesheets when we still haven't landed a leaner solution to conditionals -> https://github.com/w3c/csswg-drafts/issues/112

rachelandrew commented 6 years ago

Having done a fair amount of work writing about flow relative CSS recently, I'm a fan of the per declaration !keyword approach to this, plus possibly some way to indicate that the entire stylesheet (or section of the stylesheet) follows flow rules (and the at-rule makes sense here to me).

astearns commented 6 years ago

@fantasai should this go on the weekly agenda, since we didn't get to it at the F2F meeting?

fantasai commented 6 years ago

@astearns Targetting TPAC for this one. Rossen and I think it's better handled at an F2F, plus we'll have more i18n people available for the discussion there.

c-smile commented 6 years ago

Just in case, how this is made in Sciter:

  1. Sciter is not using logical properties.
  2. Instead I've added mapping property:
    margin: 1em 1em 1em 2em;
    mapping: left-to-right( margin );

    that above is interpreted as

    margin: 1em 2em 1em 1em;

    left-to-right(...) function accepts list of following keywords: none , inherit, all, margin, padding, border, layout, alignment and image.

Practice shows that development is made almost always in LTR mode. And then RTL is added to existing design this way:

ul:dir(rtl) { mapping: left-to-right( margin ); }
Loirooriol commented 5 years ago

A third option would be to have some other not-currently-used single punctuation character somewhere in the declaration, to indicate flow-relative mapping

The problem is that this could be used in stylesheets but not in CSSOM. I prefer a keyword specified as part of the value.

In fact I'm a bit concerned about the CSSOM implications of this thread, which haven't been discussed. Currently the margin shorthand only has the physical longhands, but with this feature margin would also map to the logical longhands.

CSSOM shorthand serialization says

If [...] shorthand cannot exactly represent the values of all the properties in list, return the empty string.

This means that margin could only be serialized if only the physical longhands are set and the logical ones are not (or viceversa). But this doesn't seem much intuitive to me:

element.style.margin = "1px 2px 3px 4px";
element.style.margin = "logical 5px 6px 7px 8px";
element.style.margin; // ""

the first line sets the physical ones, the second line the logical ones, and then the shorthand cannot represent a combination of both and serializes to the empty string. I would expect to get the most recently assigned value.

So maybe element.style.margin = "logical 5px 6px 7px 8px" should remove the previous declarations of the physical longhands? They will be overridden anyway (unless they are important). Or maybe the serialization algorithm should serialize shorthands with both logical and physical longhands if all the logical ones are set and have more precedence than the physical ones (if any), or viceversa.

css-meeting-bot commented 5 years ago

The CSS Working Group just discussed Flow-relative syntax for 'margin'-like shorthands.

The full IRC log of that discussion <fantasai> Topic: Flow-relative syntax for 'margin'-like shorthands
<emilio> Github: https://github.com/w3c/csswg-drafts/issues/1282
<r12a> s/multiple languages as a single language/multiple languages for a single font/
<emilio> fantasai: currently css assign values in shorthands to the physical longhands
<emilio> fantasai: it seems useful to make logical shorthands similarly convenient, which is useful for i18n
<emilio> fantasai: but we don't have a proposal for which kind of syntax we want to have for this
<emilio> fantasai: one's relative keyword, other is a bang keyword, others is a different property
<astearns> !no
<emilio> fantasai: one of the main restrictions is try to keep it sufficiently easy to type
<dbaron> I would be pretty strongly against a ! syntax for something that's part of the property (i.e., not changing cascading).
<emilio> fantasai: nothing on the thread seems to have stuck
<emilio> fantasai: can we come up with some idea?
<emilio> fantasai: other proposals were like global mode switchs, etc...
<emilio> fantasai: we could do some or multiple
<emilio> fantasai: [enumerates other solutions from the thread]
<emilio> addison: one of the challenges is when you go to make a rtl page layout you need to edit your stylesheet to flip your margins, ideally it'd be default
<myles_> q+
<emilio> TabAtkins: Something in the value space is maybe the best, like keywords
<emilio> TabAtkins: I'd be against punctuation, no precedent and hard to google, plus not compatible with CSSOM
<emilio> TabAtkins: similarly for the bang
<emilio> TabAtkins: nor mode switches, dbaron argued against it, serialization is also harder that way
<emilio> q+
<r12a> q+
<emilio> fantasai: typing relative is too long
<astearns> ack myles_
<dbaron> +1 to tab saying it should be in the regular value space
<emilio> myles_: Will the bang keyword be applicable on every property
<emilio> *?
<iank_> q+
<emilio> fantasai: only to some
<astearns> +1 to dbaron's +1 on regular value space
<dbaron> q+
<emilio> myles_: so part of the grammar of the specific properties, right?
<emilio> fantasai: yes
<emilio> myles_: it'd be cool if it works with variables
<emilio> fantasai: that'd be terrifying
<astearns> ack emilio
<emilio> TabAtkins: you should be able to drop a whole relative margin value in a variable
<TabAtkins> TabAtkins: And you *cannot* put bang values into variables.
<myles_> emilio: expanding shorthand into mulitple lonahands dpeending on syntax, like overflow, is not something that any othe rproperty has righ tnow. So it's goig to require specifying how yous erialize when you have all of th e8 logical and physical margins. IF you want the solution that peoplw ill implement fast, the best option is different properties. Then, all the machinery is there already.
<myles_> fantasai: how do we come up with a systematic way of coming up with a new property that is consistent, short, easy
<myles_> emilio: yes
<myles_> TabAtkins: we do "margin-se"
<myles_> emilio: or "logical-margin"
<myles_> fantasai: it's way too long. some peopl will put all of their stylesheets all of the time, and will stop using physical properties. It needs to be ergonomic enough that it's possible
<dbaron> 'margins', 'paddings' :-P
<myles_> florian: will we push back against tab here about puctuation? ~padding ~margin?
<jensimmons> it might be faster to implement, but if we hate the choice 10 years from now, it’s not a good idea
<myles_> TabAtkins: if its property names, my objection is different. The only thing is that would not be an ident anymore.
<myles_> florian: can we fix it?
<myles_> TabAtkins: potentially but its a syntax change. It would be better for it to fit kn the syntax.
<myles_> florian: if somebody is using a property that uses idents, this would break.
<myles_> TabAtkins: we try to not change syntax
<myles_> florian: this may warrant an exception
<myles_> TabAtkins: yes. I think we can come up with a prefix in alphabumeric that's short
<emilio> astearns: do we have anything more on that?
<astearns> ack r12a
<emilio> r12a: I agree with that fantasai and TabAtkins that it needs to be easy to type, I'd suggest `lmargin`
<jensimmons> q+
<myles_> +1 to not using "relative"
<emilio> r12a: I think `logical` is a much better word than `relative`
<cbiesinger> +1 for not using relative
<astearns> ack iank_
<emilio> iank_: Any of the bang syntax will probably have very funky interactions with CSSOM
<TabAtkins> I think `l` as a prefix works okay. I think we can reasonably prefix any word with that.
<emilio> iank_: setProperty has a different argument for `!important` and such
<astearns> ack dbaron
<fantasai> I don't like prefixing with alphanumeric because it's less obvious what's going on and it won't sort correctly.
<TabAtkins> *post*fixing with `l`?
<fantasai> we use prefixes for the real name of the property, and prefix relationships are about shorthands
<fantasai> postfixing makes it look like a different longhand of the physical shorthand
<astearns> q?
<emilio> dbaron: So I think we all don't like the various bang things. I guess I'm not 100% convinced we want different property names, I think having it in the value would be slightly nicer even if we need to sort out a bunch of CSSOM issues, though it might depend on whether we find appropriate names for the properties
<astearns> ack jensimmons
<emilio> dbaron: different properties is probably faster
<cbiesinger> q+
<emilio> jensimmons: some of the things suggested where the good syntax is the old thing that nobody uses 10 years from now
<emilio> jensimmons: so even if might be more efficient or easier to implement we should peek a name that is a good name
<majidvp> q+
<emilio> heycam: I feel like all of the syntax proposed so far is going to be a bit different and awkward, so I'm not sure the goal of finding a clean word is feasible
<emilio> jensimmons: I think lmargin is a bit more awkward than relative on the value
<dbaron> I guess another option is using a delimiter within the 'margin' value like 1em / 2em / 3em / 4em.
<emilio> jensimmons: Some of them feels smoother than others
<r12a> q+
<rachelandrew> q+
<emilio> fantasai: can you give us your opinion on the different proposal?
<emilio> jensimmons: I think -new is better than a new name, and keyword is better than a bang, but I can look at the list as we goo
<astearns> ack fantasai
<emilio> jensimmons: how does it look in 10 years is something to look into
<TabAtkins> schmlinss
<myles_> jensimmons: sticking a random letter in front of iframe hasn't seemed to hinder its adoption, it seems most people colloquially speak of iframes instead of frames
<emilio> fantasai: prefixing breaks the sort order, margin-something feels like a longhand analogous to margin-left or such
<jensimmons> iMargin? lol
<emilio> fantasai: I'm a little skeptical about prefixing / suffixing has the issue of making it relate to properties it doesn't relate to
<dbaron> q+
<emilio> *seem to relate
<astearns> ack cbiesinger
<TabAtkins> Allowed postfix punctuators in ident syntax: - or _ ^_^
<TabAtkins> margin-, margin_
<myles_> margin: ➡️
<emilio> cbiesinger: I think lmargin and such are a huge concern, we have some other messy margin
<emilio> majidvp: I think mode switch is the easiest. Is the concern about serialization really a problem? When do you have the problem?
<jensimmons> imargin = international margin
<cbiesinger> @mode "logical"; at the top?
<fantasai> cbiesinger, yes that's one option
<emilio> florian: we have some of that problem with box-sizing and such
<fantasai> cbiesinger, the other is a block like @media
<cbiesinger> block seems worse to me
<fantasai> cbiesinger, yeah I agree
<emilio> dbaron: I think we consider it a design mistake
<fantasai> cbiesinger, if we have a mode switch we might want to have a way to put a specific declaration into the other mode, though
<astearns> ack r12a
<emilio> s/it/box-sizing
<astearns> ack majidvp
<fantasai> s/consider it/consider box-sizing/
<emilio> r12a: dbaron proposed separators instead, maybe we should consider that?
<cbiesinger> fantasai: maybe but your escape hatch is margin-left
<astearns> ack rachelandrew
<fantasai> s/fantasai:/fantasai,/
<emilio> rachelandrew: From the POV of teaching this a keyword means that you can look at your code and know what I'm using here, seems to infer the intent best
<emilio> dbaron: I suggested using separators, assuming we do want to move everyone to that, so that you can do something like `margin: 1em / 2em / 3em / 4em`
<emilio> dbaron: It's somewhat obscure so it makes the distinction less obvious, but if it becomes the way to do it it may not be a problem/
<jensimmons> What Rachel just said makes me think that `margin: 10px 5px 15px 25px relative;` is more of a “equal” to `margin` today… changing to a different property infers that it’s a different thing
<emilio> fantasai: we use slashes on some places already, so not sure we could switch everyhwere
<r12a> q+
<astearns> ack dbaron
<emilio> dbaron: it doesn't need to be a slash, there are probably a number of those we haven't used yet
<cbiesinger> +1 for dbaron's suggestion
<emilio> dbaron: the other reason it sort of makes sense to me is that the delimiter indicates a different relationship between the different values
<jensimmons> also, should we go the other way — to match Grid — …. ???? Crazy. but also....
<emilio> myles_: looks like if you don't use slashes code work and it'd look just wrong in RTL
<fantasai> yes, jensimmons, we will go the other way to match Grid
<astearns> ack r12a
<emilio> TabAtkins: Since the direction is different it at least sometimes will look wrong
<fantasai> Grid is using the direction we picked out for logical 4-value syntaxes in general, we just never solved this particular syntax issue to have them :)
<emilio> r12a: So you still have to deal with serialization and such
<emilio> dbaron: yeah, that was emilio's concern, we need to solve that if we handle margins
<fantasai> It's interesting to note that the grid-area shorthand already uses slashes (and is logical)
<emilio> emilio: You also need to handle compressing and serializing when you specify all the 8 margins
<emilio> dbaron: you could condense those to two occurences to the margin shorthand, but it's a bunch of CSSOM work
<rachelandrew> there is an example of individual properties here for padding: https://codepen.io/rachelandrew/pen/OQrorW
<emilio> dbaron: that's the 'this will take longer to get done'
<rachelandrew> from https://www.smashingmagazine.com/2018/03/understanding-logical-properties-values/
<emilio> emilio: note that you also need to figure out how that interacts with the other sub-shorthands like margin-block / margin-inline
<emilio> TabAtkins: yeah I think finding something using property names would be maybe the better idea
<Bert> q+
<emilio> fantasai: I think you still need a switch to change to physical in specific cases, and whatever solution we choose needs to be workable for that
<fantasai> myles_: Mode switch at the top of the file, many CSS authors don't know CSS that well and just copy-paste, so mode switches would end up being problematic for them
<astearns> ack Bert
<emilio> myles_: It's harder for authors to understand mode-switches, and they'll just get it wrong
<fantasai> Bert: Said earlier that it might be a problem if margin expands to different properties based on whether there's a keyword or not. Is that true? Wouldn't it expand to all of them all the time.
<emilio> fantasai: thanks, I had missed it :-)
<fantasai> florian: Depending on the values, they are propagated to different values, but always expand out to the same set of longhands (all of them)
<fantasai> dbaron: At the specified value it is, but at the computed value level, the two sets of properties compute the same values
<fantasai> dbaron: The way we've added logical properties, they are distinct properties at the specified value level nd they both exist in the object model
<fantasai> Bert: If you set margin-start, it goes to margin-start
<fantasai> Bert: If you set 'margin, does it also set margin-start or only go to margin-top
<fantasai> dbaron: We're rying to find a syntax that sets the margin-start property
<fantasai> Bert: Is it necessary?
<fantasai> florian: ...
<fantasai> florian: We don't have a logical shorthand. If we want logical to be the default way to write style sheets, we need that.
<fantasai> astearns: Short answer is no the shrothand doesn't expand to all 8
<fantasai> dbaron: Having the shorthand somehow set margin-top so that it sets the logical thing, could be doable, but would have some very confusing results
<fantasai> florian: I didn't understnad that
<fantasai> Bert: Problem is that the 'margin' property doesn't reset the logical margins. Can that be changed?
<fantasai> Bert: Like font resets font-size-adjust even tough it's not mentioned
<fantasai> fremy: I'm not sure why it doesn't set
<fantasai> fremy: These values would never be used
<fantasai> fremy: It doesn't matter if margin resets them or doesn't
<fantasai> fremy: If the orde rin which you reset them is such that you have the logical ones after the other ones its fine.
<fantasai> dbaron: That would be one solution to part of the object model issues.
<fantasai> dbaron: others around serialization
<fantasai> fremy: Somewhere we have to find principles of doing this, and need algorithm for this
<fantasai> fremy: I ddin't find it yet
<fantasai> dbaron: Might be in a GH issue somewhere
<fantasai> dbaron: probably don't want to dig too far into CSSOM issues right now
<fantasai> astearns: This was a good background on all solutions we could consider, but doesn't sound like we'll resolve today
<fantasai> TabAtkins: Sounds like we're interested in a declaration-based syntax
<TabAtkins> More specific than declaration-level. Property-name or value-level. (So not declaration glyphs, or ! values, etc.)
<florian> fantasai: I think whether or not we need a mode switch, we will have a syntax ???
<myles_> "use logical;"
<myles_> (instead of "use strict")
<emilio> lol
<florian> fantasai: the reason the shorthand resets 4 or 8 of the longhands is actually still an open issue
<florian> s/the reason/whether/
Dan503 commented 5 years ago

This is highly reminiscent of the box-sizing issue where most people by default want to go all in on a particular box sizing mode but want to be able to sometimes switch back to the other mode for things like 3rd party components.

The best practice for handling box-sizing at the moment is this:

*, *::before, *::after { box-sizing: inherit; }
html { box-sizing: border-box; }

People are pretty happy with that syntax and it pretty much solves all the box-sizing use cases.

So based on what we know about how people have implemented box-sizing, I think adding a box-mode: physical | relative; property is best and ensure that it inherits.

That way people can write a CSS reset like this:

html { box-mode: relative; }
.not-relative { box-mode: physical; }

If we expect relative margins and paddings and so on to become the new standard that everyone uses then we should take inspiration from how people use the box-sizing property because it has already walked that path.

Imagine if instead of the box-sizing property, we had to write this on practically every width and height setting:

.example {
  width: 100% !border-box;
  height: 100% !border-box;
}

That would be aweful, you would have hundreds of !border-box all throughout the style sheet.

That's why I think we should have a CSS property that can globally set this for us. Follow the path that box-sizing has already blazed for us. This time it should inherit though. This is so that we have an easy way to make old 3rd party components designed to work with physical margins not break in our new relative margin style sheets. Inheritance means that we wont have to resort to a *, *::before, *::after rule.

Loirooriol commented 5 years ago

People are pretty happy with that syntax and it pretty much solves all the box-sizing use cases.

I had problems with that when I added a border to an image, it shrank the image and made it blurry. Also, it's an ugly hack. I prefer a content-box box-sizing by default, and only opt in to border-box when I really want it.

I think adding a box-mode: physical | relative; property is best

I don't think this can really work. The difference is that box-sizing only affects layout, not the cascade.

The logical longhands are different and independent properties from the physical ones. When you have a shorthand, you need to know to which longhands it expands in order to set their specified values. But at that time you don't know the computed value of box-mode yet!

jdsteinbach commented 5 years ago

Has a new CSS function been considered? This would allow the TRBL shorthand in any property to work (without duplicating all the property names that use the shorthand):

.example {
  margin: logical(1em 2em 20px);
  padding: logical(.5em 1em);
  border-width: logical(0 0 1px 1px);
}
elad2412 commented 5 years ago

I think let's make life easier! The Idea(use some of the idea of @fantasai and @chharvey ): add a new property that will declare the type of properties and will have inherit as default. add now all the page will be according to the HTML definition.

flow-mode: physical /default value/ or logical;

html{
   flow-mode:physical; 
       /*or*/
   flow-mode:logical;
}

.box{
  /*will be according to the HTML flow-mode value*/
   margin:10px 5px 6px 3px;
  padding:5px 10px 2px 7px;
}
Loirooriol commented 5 years ago

The problem is that it's bad if you need to know whether the shorthand will expand into physical OR logical longhands at specified-value time, but this depends on the computed value of flow-mode (or writing-mode in your second idea, but this would be too hacky).

However, something similar to https://drafts.csswg.org/css-variables-1/#variables-in-shorthands could be done:

It think this would more or less work, but it would be a breaking change:

element.style.margin = "1px 2px 3px 4px";
element.style.marginTop;

Currently it produces "1px", but would become "" because at specified-value time it's not known whether the 1px will be set to margin-top or margin-block-start.

So IMO it would be simpler and less problematic to decide between logical or physical via syntax instead of via the computed value of another property.

argyleink commented 4 years ago

What if the scope of the request to opt into logical resolutions was file scoped? Then it couldn't leak out into other uses of margin (imports, 3rd party styles, etc) while still allowing current shorthand to upgrade.

Here's a terrible example but hopefully it articulates the intent for us to talk and bikeshed about it?

@logical;

p {
  margin: 1ex 1ch;  
}

/* 
p {
  block-start: 1ex; 
  inline-end: 1ch; 
  block-end: 1ex; 
  inline-start: 1ch; 
}
*/

Open questions:

  1. do we have any file scoping mechanisms at the moment or is this asking for a lot of browser internal work?
  2. is leaking only something I'm concerned about when thinking about options like
    html { 
      flow-mode: logical;
    }

I'd also love to see this thread spin up again! Share your thoughts 👍

una commented 4 years ago

I think it makes sense to set the flow-mode on an element instead of on a file. File settings aren't common in CSS like they are in JS and feel more arbitrary than setting flow-mode on a specific parent. We shouldn't be conflating file architecture with flow intention, especially since a common CSS architecture includes includes (no pun intended). This feels more confusing than it needs to be.

I feel that the example from @elad2412 makes the most sense in this case, with

html{
   flow-mode:physical; 
       /*or*/
   flow-mode:logical;
}

I don't think this necessarily competes with element.style.margin = "1px 2px 3px 4px";. That would still be valid, inclusive of flow-mode.

Loirooriol commented 4 years ago

I don't think this necessarily competes with element.style.margin = "1px 2px 3px 4px";

@una What if then you read element.style.marginTop? How do we expand the margin shorthand at specified-value time if the expansion depends on the computed value of flow-mode?

una commented 4 years ago

@Loirooriol

@una What if then you read element.style.marginTop? How do we expand the margin shorthand at specified-value time if the expansion depends on the computed value of flow-mode?

element.style.marginTop would act like marginStart if the parent has a flow-mode: logical.

Loirooriol commented 4 years ago

Ah, so you want the expansion to depend on the computed flow-mode of the parent element, instead of the element itself? Then, what if the flow-mode of the parent changes after expanding margin: 1px 2px 3px 4px? Should we keep track of the original shorthand declaration, so that it can be expanded again with the new mode? Or should we just keep the longhands of the old expansion?

una commented 4 years ago

@Loirooriol yes, I would expect if at any point, logical flow was added to the parent, the margins would become logical. This would also make it easier to port over codebases that set styles in JS to logical properties.

Order wouldn't matter here in terms of when the properties are adjusted -- the browser would parse the margins differently based on flow-mode. flow-mode will inherit to the parent, unless it is reset on the given element, so it's possible to have logical flow in a parent and physical flow in a child.

For the expansion example, the expanded sequence will always be rendered in the same flow-mode as any other margin set on the element, so it shouldn't collide. This is true for physical margin properties (top, left, bottom, right) as well ad logical properties.

In the case that flow-mode is set to physical, and a logical value is set (margin-inline-start, margin-block-start, etc.), the logical value will be converted to its physical counterpart of margin-left, margin-top, etc.

When no flow-mode is set on any element up the tree, the default flow modes for margin-top vs. margin-block-start apply.

faceless2 commented 4 years ago

Funny, I hadn't seen this thread until its resurrection. I've recently been dealing with the equivalent properties in PDF, which are always ordered in logical order - and personally, I hate it. Mainly for the reason that @Loirooriol raises two posts above - the meaning of the property value now cannot be determined without going up through the hierarchy to check the writing-mode of the ancestors. It makes it harder to visually inspect CSS to know what it means, it makes it harder to move nodes around in the tree, and it makes it harder to import styles from external sources.

This last point matters; if we were doing something like this 20 years ago it would be different. But I think it's fair to say there's an established body of CSS out there that uses the margin shorthand. If the way that property is parsed can now be changed by altering an ancestor, I predict that for someone, somewhere, it will cause an awful lot of pain (note I'm presuming that flow-mode is inherited, which seems to be the intent).

For that reason, I agree 100% with @Loirooriol's points above, and personally would lean towards @fantasai's original proposal of using a magic ident: margin: logical 1px 2px 3px 4px or similar. If you have to do this every time you want to specify margins, paddings or borders in logical order, it's a bit more verbose. That sucks, but only a little. However on the plus side, it's completely bulletproof against unintended side-effects.

r12a commented 4 years ago

Logical shorthands really ought to be used by everyone, every time they set up margins, because even if content is in a horizontal, LTR text flow it may be translated into something else that isn't. I've been thinking how to make it as easy as possible for content authors to switch.

Ideally, we'd just make margin adopt logical rather than physical ordering. However, although it would probably make less breakage than you might expect, it will probably still cause some. So it would be good to come up with something that is as close to that as possible, and is very simple to type.

Initially, typing 'logical' appealed to me, since it's clear, but then i realised that it positions this approach as the exception, rather than the rule. It's also a pain to type 'logical' every time you want to define a margin with a shorthand (which for me is much of the time).

So how about something like creating an lmargin property name? It's 1 extra character only. Maybe that's as close as we can get to making this painless for content authors to adopt?

MurakamiShinyu commented 4 years ago

I wrote a similar idea, https://github.com/w3c/csswg-drafts/issues/1282#issuecomment-298143962:

How about margin-bi (suffix -bi, stands for "block and inline")? I know this is exceptional in CSS property naming convention (avoid abbreviations and use complete words), but has the following advantages:

  • easy to type, only three additional characters to the original name
  • we have *-block and *-inline properties, and making the combined shorthand names using the first letters of "block" and "inline" will be easy to understand
  • "bi" indicates the order of values, block is first then inline, and convenient to remember the value syntax

I think the naming margin-* is better than lmargin because:

If "-bi" suffix is not good, other candidates would be:

r12a commented 3 years ago

we have already margin-block and margin-inline shorthand properties, and the naming lmargin is not consistent with those.

Point taken.

lmargin will be mistaken for the left margin.

Possibly, though i think people will learn pretty quickly.

when property names are alphabetically sorted, lmargin and other margin properties become separated, so suffix is better than prefix.

Good point.

If "-bi" suffix is not good, other candidates would be:

Hmm. I think the shorter the better. Personally i'd much rather not reach for the hyphen, if possible. (I often miss.)

Dan503 commented 3 years ago

What about margin-logical: 10px 20px?

I think that is better than margin: logical 10px 20px.

margin-logical can be auto-completed with autocomplete in every editor that features CSS autocomplete. It also aligns with margin-block and margin-inline.

margin: logical on the other hand is much less likely to be widely supported in CSS autocompletion implications.

normanr commented 3 years ago

Building on @argyleink's suggestion, it could also be written as a nested at-rule, eg:

@flow-mode(logical) {
  p {
    margin: 1ex 1ch;  
  }
}
r12a commented 3 years ago

margin-logical can be auto-completed with autocomplete in every editor that features CSS autocomplete. It also aligns with margin-block and margin-inline.

After consideration, i do rather like this, even though it has a hyphen, for the reasons @Dan503 mentions. It also gets around the difficulties of finding single letter abbreviations that convey the intended meaning.

mirisuzanne commented 2 years ago

Looking into this more with @fantasai and @jensimmons, we think it's important to start this conversation with the long-term future in mind:

Goal: make primarily flow-relative stylesheets as easy and pleasant to author as stylesheets using primarily physical coordinates.

The first important question to consider is whether or not the CSSWG can adopt this goal. This is harder than just making it possible to author in flow-relative coordinates (but we think it's worth it). Long term, we hope that flow-aware styles and layouts can become the default that all authors reach for when starting a new project. With tools & browsers now automating the translation of entire web pages, authors should be encouraged to create flow-relative layouts, even when the site itself does not contain bidirectional content.

Use cases for logical-first authoring:

End State

While adding *-logical variants of each property seems reasonable as a short-term solution for margin and a few other properties (authors can use auto-complete to write long names), that becomes significantly more burden when extended to entire logical-first stylesheets including all the potentially affected properties and function syntaxes (box properties, <position> syntaxes, transform functions, etc.). Stylesheets written logically would be peppered with logical annotations, making them noisy to read and more work to type out—relegating logical styles to second-class status.

Therefore, to make logical-first stylesheets equally authorable, @fantasai and I believe we would ultimately need some kind of higher-level lexical switch.

Overall the proposal that seems to make the most sense is to provide:

For example:

<coordinate-mode> = [ logical | physical ] or [ relative | absolute ] or ...

@mode <coordinate-mode>; /* must come after @import and before any style rules */

@mode <coordinate-mode> { <stylesheet> }

selector {
  property: value  !<coordinate-mode>;
}

Note: A mode switch that is not lexically scoped would cause declarations written without knowledge of this style sheet to be re-interpreted in an unexpected coordinate mode. This is bad.

Note: A mode switch (like box-sizing) that is attached to an element is also bad. In addition to the issues with global scope (previous note), it would require cascading the mode switch property before any shorthand declarations can even be parsed, nevermind cascaded.

Roadmap

It's not possible to instantly implement logical shorthands for all affected properties and functional notations across the entire language, so realistically speaking moving to this new world is a multi-year project, consisting of several steps:

  1. Adopt per-declaration syntax switch, to be defined as valid on a property-by-property basis.
  2. Make sure everything that can have logical/physical variants has both. (Years-long process.)
  3. Adopt @rule for switching syntax at a higher level.

Note: For compatibility reasons, we can't adopt an @rule until we've defined the impact of switching every declaration to logical mode.

First steps

Property by property, function by function, we would need to adopt a convention that takes notations with bare coordinates (that are not explicitly associated with an axis or side) and gives them an explicit coordinate space (physical or logical).

Whatever syntactic convention we adopt for this, it needs to:

Using !keyword fits these requirements. Appending -logical or -physical to property or function names fits these requirements. Using a bare keyword embedded in the value space does not.

r12a commented 2 years ago

Just curious: mode as described above seems to be defined as a dedicated physical vs. logical switch. Are there other modes that one might want to apply to the stylesheet, such that you might end up in the future choosing one of physical/logical, but also adding other keywords to the list?