w3c/csswg-drafts

[css-display-4] Do we need `reading-order: <integer>` or should `reading-order: auto` be allowable in all grid or flex layouts?

rachelandrew opened this issue · 20 comments

This issue relates to the edits to CSS Display 4 based on this dicussion, and the as-yet unmerged PR at #8257.

TL:DR is that we are wondering if we only need reading-order:auto (on the children) and if there are reasons to do reading-order: <integer> at all, or initially. Do compelling enough use cases exist to have that level of control or do people really just want to follow the layout?

Longer notes below:

My original suggestion was a switch on the grid/flex container. In that scenario all of the following would follow the grid or flex modified order:

  • flex layout with order
  • flex layout with *-reverse
  • grid layout doing placement with lines/template areas
  • grid layout with order
  • "randomized grid layouts" with dense packing or masonry

In the existing edits plus this PR, the reading-order: auto value is applied to the children, and only covers:

"randomized grid layouts" with dense packing (and I'm assuming masonry)

To do the following you would need reading-order: <integer> on the children:

  • flex layout with order
  • flex layout with *-reverse
  • grid layout doing placement with lines/template areas
  • grid layout with order

There are definitely use cases for people wanting to follow the layout created by placement (usually because they want different layouts at different responsive breakpoints), so if we only do auto as specced, we don't have a method to achieve those use cases, and if we go for the switch on the container that likely stops us doing this version later.

An alternative, however, would be if the only value for reading-order was auto (applied to the children) and auto applied in all layout types, not just the randomized ones. As that shouldn't roadblock us from adding the integer value in the future.

Reading order and layout order being separated, is a use case. For example, this, but I think in that case, if we went with auto applied everywhere that doesn't need to affect layout-order with no reading-order.

I feel that having the property on the children is better than my idea of a switch on the parent (though I guess we could then add a switch on the parent that sets them all as a group, as with align-items), because it allows for more flexibility, but I think we could get away with that initially being auto only.

The other part of the discussion on Monday was the idea that there are multiple ways one could want to auto-order certain layouts. A flexbox might want to go in flex order (source order + 'order'), or in logical order (start from the startmost flex line, and the startmost item in it, then proceed, so 'flex-direction' and 'flex-wrap' also affect things). A grid might want to be read in column-major order or row-major order. Etc.

It sounds like that's something that would be indicated on the container itself.

So I think my proposal is that we should keep the current spec as written, but add a property on the container (reading-order-order, clearly) that controls how we assign the ordering to reading-order: auto children. Give it an initial value that only has an effect on randomized layouts, and then we can extend it with specific-layout keywords for those other cases as we come up with them.

And by "we can extend it" I mean we should add at least one keyword per major layout type right now - block, table, flex, grid. This allows authors to get the "just do it right" behavior, but doesn't allow them to just set it on *, since you do have to at least think enough about the individual elements to give them the correct reading-order-order value. I think that's an ok balance of friction vs usefulness?

@tabatkins so in the case of having (for example) reading-order-order: grid row this would allow a non-randomized grid to opt into layout ordering, without needing the reading-order property on the children?

Right, exactly. (And it would do nothing on, say, a flexbox.)

And for a non-ridiculous name suggestion, maybe reading-order-items, paralleling the "set how normal behaves on children" behavior of place-items?

Yeah reading-order-items works.

This still leaves us with the question as to whether we need reading-order: <integer> at all. If we were to implement reading-order-items on the parent and reading-order: auto for the children, would that help with @fantasai's concerns of people just applying it everywhere?

Could we initially drop reading-order: integer and then bring it back if we discovered need for it?

kizu commented

As a developer I'm totally ok to first have only the auto options (and I'm ok with “one keyword per major layout type” limitation) and dropping the integer for now.

I think there might be use-cases for the integers, but it can be quite a complicated issue, where things should be thought through quite extensively in order to replicate the mistakes of a tabindex, stacking context etc. So better to first have something that would work for more simple cases, and in the meantime see how the more complex cases could be handled.

I don't really get when anyone would need the reading order to be different on individual children. It seems simpler (and shorter and easier to remember the property name) to just have reading-order set on the parent to affect the children's reading order, and not try to tie this into the order property.

The -items vs. -content thing on align and justify is a major headache already, and I'd rather not have to remember which one to use for yet another property.

Proposed syntax:

reading-order-items: normal | flex [ visual | flow ] | grid [ rows | columns ]

normal means to use DOM order. flex only works on flex containers; it either traverses the rows visually (based on writing modes, so left-to-right or top-to-bottom for English) or per flex-flow. grid only works on grid containers, it traverses the grid row-major or column-major.

In either of the latter cases, the 'order' property is taken into account, just like any other flex/grid layout property that affects where the element ends up. So an order: -1 element sliding to the front of the flexbox will make it first in flex flow as well (and possibly in flex visual, depending).

(Do we want a "DOM order, but let order take effect as well" value? That lets you stick with the "I made a good DOM order, just follow it" behavior of normal, but allows small tweaks for layout breakpoints. If so, I propose from-order.)


Per issue discussion, the reading-order property itself is no longer necessary; since we're dropping the integer value there's only the auto value left. We can put it back if we ever need it.

I think this works.

(Do we want a "DOM order, but let order take effect as well" value? That lets you stick with the "I made a good DOM order, just follow it" behavior of normal, but allows small tweaks for layout breakpoints. If so, I propose from-order.)

So that would give the options:

  • I changed the order and want the visual and reading order to be different (don't use reading-order-items.)
  • I changed the order and want the visual and reading order to be the same and take order into account (use reading-order-items and the flex or grid keyword values)
  • I changed the order and want the reading order to follow the order-modified order but no other changes from the flex or grid layout (use reading-order-items: normal from-order)

That seems useful, and worth adding.

Some comments:

  • Grid has a grid-auto-flow, should there be a keyword that simply matches it?
  • I'm not sure having the multiple-keyword syntax is helpful, maybe these can just be hyphenated?
  • Relatedly, maybe reading-flow is a reasonable name, since it relates to flex-flow and grid-auto-flow?
  • I'm hesitant to have order overriding DOM in the reading order unless the author very explicitly requests it, because the main purpose of order is to diverge logical and spatial order. So I don't want to do that without the author opting in intentionally.
  • I can also see use cases for wanting a reading order that pulls some items out of the background coordinate-based flow (e.g. in a grid) and makes them first or last in the reading order while leaving them in the middle of the visual flow. Imagine, for example, a grid of small cards with a few pulled out and enlarged 2x2: a visual navigator would perceive those 2x2 cards first, and then the rest of them, so you might want to match the reading order to that. (Or you might not, depends what you're doing.)

It would be very nice to just add a * { reading-order-items: I-dont-wanna-think-about-this; } to new projects and it follows grid or flex or whatever and I don't have to worry about it again unless I specifically want to do something else in an unusual situation.

@fantasai I would expect order to change the reading order too because it changes the visual order and I've requested the reading order to follow the visual order. There are uses for manually diverging visual and reading order but that should be a separate dial to turn.

I'm hesitant to have order overriding DOM in the reading order unless the author very explicitly requests it, because the main purpose of order is to diverge logical and spatial order. So I don't want to do that without the author opting in intentionally.

I'm not sure quite what you mean by this.

In the flex/grid cases, DOM order is already completely lost. Other than its incidental effects on how elements get automatically ordered, there's nothing left there. Visual order inherently and necessarily is post-order.

In the "DOM order, but let order take effect as well" example I was asking about, it would also be an intentional opt-in.

I can also see use cases for wanting a reading order that pulls some items out of the background coordinate-based flow (e.g. in a grid) and makes them first or last in the reading order while leaving them in the middle of the visual flow. Imagine, for example, a grid of small cards with a few pulled out and enlarged 2x2: a visual navigator would perceive those 2x2 cards first, and then the rest of them, so you might want to match the reading order to that. (Or you might not, depends what you're doing.)

This is the reading-order: <integer> case that we're explicitly discarding for now, right? (Tho the "DOM order, but pay attention to order" would also allow this if the call-outs were explicitly positioned, as they could then use order without actually affecting their position, just their paint layering.)

It would be very nice to just add a * { reading-order-items: I-dont-wanna-think-about-this; } to new projects and it follows grid or flex or whatever and I don't have to worry about it again unless I specifically want to do something else in an unusual situation.

We can't do this, unfortunately. There simply isn't a single correct answer for the elements that have this ability. We also somewhat want to encourage this to be something you think about a little bit. We don't need much, but it's good to have some deliberateness.

It doesn't need to be a single correct answer it just needs to be a better default than the status quo. If there were a reading-order-items: auto meaning normal, flex, or grid depending on display it would be correct almost all of the time and it's easy to override when it's not. Otherwise you need to add this every time you use grid/flex in a way that changes the reading order so you can't tell at a glance whether "oh did they forget this" or "oh they didn't need that". This is a box-sizing: border-box thing. There's not a perfect answer but it's border-box.

While I like giving power to the designer/developer, I think we should strive towards keeping this simple, without excessive options.

I also think we should decouple reading-order-items from a direct association with order, even in the property name. I like reading-flow. It would be inheritable, and affect tabbing order (when tab-index is not an integer greater than 0) and any other sequential access such as screen reading order. We might even let it affect the initial scroll position of a scroll pane (not sure if that is a sensible thing to do or not).

Having a separate reading-order: <index> property for tweaking individual children’s reading order separately from order should really be a completely separate issue, IMO, and not considered in the design or naming of the parent element’s reading flow property.

I also think we shouldn’t have values like flex that only apply apply when you have flex layout. It would be better to have “layout reading flow” value that can be set at the top of a tree and inherit down into whatever layout types exist within its decendants, including tables, grid, flex, and maybe floats. I don’t think negative margins, transforms, or abs-pos/fixed positioning should have any effect on how it works (it doesn’t seem necessary, and probably overly complicated for all parties, and would probably lead to behavior that is surprising for authors and users).

So my proposed syntax is:

reading-flow: normal | layout | cross-layout

Value meanings:

  • normal: DOM order, as it is today.

  • layout : After layout, including the effects of order, grid-auto-flow, the grid-area properties, the flex-flow properties, and float. Reading and tabbing happens in the direction of flex-flow for flex-layout, and in the direction of grid-auto-flow for grid. That determines whether you start with columns vs. rows in grid and table (if grid-template-rows is masonry, then reading starts with columns, and vice versa). The reading/tabbing order within each row or column would be from start to end (inline first, then block), AFTER order and the grid-area properties (and any dense packing) have determined the layout arrangement. The -reverse suffix for flex-direction and flex-wrap would have no affect on the direction of reading/tabbing (because that would be weird).

    This also means that the cells of tables would would be in accessed in writing-mode order, e.g. starting in the lower right corner for vertical-rl writing mode. If the author didn’t want that, they could control it with something like this: html, td {reading-flow: layout } table { reading-flow: normal }

    For floats, items floated to the inline-start side are read/tabbed to before other items or text in the line, and items floated to the inline-end side are read/tabbed to after other items or text in the line. (P.S. I don’t actually know how float: left|right works in vertical layouts.)

    Even if a table-cell or grid item or flex item or float is not focusable, if this property causes them to move up or down in the reading order, then anything focusable in the contents of those items would also move up or down in the tab order. Tabbing is still from outwards to inwards if both an ancestor and decendant can both be focused.

  • cross-layout : same as layout, but in the cross direction for non-masonry grid and tables. For all other layout modes, this value is indistinguishable from layout.

As mentioned above, I can’t really imagine a likely case in which you would want to read or tab in a direction that is opposite of the reading mode/direction. So in English, if you are reading by column (because of e.g. reading-flow: layout; grid-auto-flow: column for grid, or reading-flow: cross-layout for table), it would be top to bottom, and if you are reading by row it would be left to right.

I had a look through the initial examples of this that developers had raised (under examples from the community n the initial issue). The things people are wanting to do seem solved by the proposed solution.

Tweaks, for example to form layout, when source is not modifiable. This isn't an ideal use, and advice would be to reorder the HTML where possible, but people are going to do this sort of thing. However this could be solved by changing the layout in grid then asking the reading order to follow that order.

Different layouts for different screen sizes. This is the use case that comes up most often, and is solved by the current suggestion, assuming the grid layout is adjusted in the media/container query and the reading-order-items property applied at the same time.

Deliberate reordering, where you want the source and display order to be different and this tweet still works as now, as you just don't use reading-order-items in that circumstance.

I can't think of, nor have I encountered from the community, an instance where we need <reading-order: <integer>. It feels as if this simpler approach solves the use cases I'm trying to solve for. It also doesn't block us moving forward with the <integer> case in future if such a need arises.

Also, to respond to @bradkemper we are only considering doing this in flex and grid contexts. I don't think there's a compelling need to do this outside of those contexts. Yes you can disconnect the source and reading order by abspos etc. but I've not seen people having the issues there that they have with grid and flex layouts. This only came up as a developer request once we had layout methods that allowed easy reordering.

kizu commented

I think we would be totally fine without the numeric order.

Yes you can disconnect the source and reading order by abspos etc. but I've not seen people having the issues there that they have with grid and flex layouts.

I expect this could potentially be more of an issue with anchor positioning and with Popover API used for it, where the anchor and its popover (or an anchor and a sidenote as in the other use case for anchor positioning) could be separated. I did create a separate issue to tackle this: #9356 — not sure if the reading-order-items itself could be applied to the abspos, as with it we usually want to move a certain element in the reading and tab order to a certain place, rather than reorder a set of items. But I think it is still worth to keep the abspos case with anchor positioning (where we could utilize the default anchor to determine the reading order adjustment) in mind.

The CSS Working Group just discussed [css-display-4] Do we need `reading-order: <integer>` or should `reading-order: auto` be allowable in all grid or flex layouts?, and agreed to the following:

  • RESOLVED: add reading-order-items into the ED and remove reading-order:<integer>
The full IRC log of that discussion <TabAtkins> rachelandrew: WE're trying to solve a problem with flex and grid
<TabAtkins> rachelandrew: Devs can easily jumble up the order of things, reading and visual order different from the DOM
<TabAtkins> rachelandrew: But DOM order is used by tabbing/etc
<TabAtkins> rachelandrew: It's usually correct to tell authors to make the DOM order match the visual order
<TabAtkins> rachelandrew: There are some cases where it doesn't tho
<TabAtkins> rachelandrew: Like, responsive design - optimal reading order in one presentation is different from optimal order in another
<TabAtkins> rachelandrew: So people want to shuffle things by MQ
<TabAtkins> rachelandrew: We're tyring to solve this by letting authors specify the reading order, when it's differetn from the DOM
<TabAtkins> rachelandrew: This particular issue, elika proposed a `reading-order: <integer>`
<TabAtkins> rachelandrew: This would specifically set the reading order to match
<TabAtkins> rachelandrew: As we worked thru the use-cases we realized devs probably didn't need it
<TabAtkins> rachelandrew: Instsead they just want "follow the flex layout" or "follow the grid layout"
<TabAtkins> rachelandrew: But there are still some switches on that
<TabAtkins> rachelandrew: So here Tab proposed to remove the integer and instead have `reading-order-items: normal | flex [ visual | flow ] | grid [ rows | columns ]`
<TabAtkins> rachelandrew: I think this covers all the use-cases
<TabAtkins> rachelandrew: I think it also wouldn't stop us from doing the integer in the future
<TabAtkins> rachelandrew: If we realize we need it
<TabAtkins> rachelandrew: So issue is, can we resolve to drop the integer and just use reading-order-items?
<TabAtkins> q+
<TabAtkins> Janina: Interesting! Sounds like we might get our cake and eat it too.
<TabAtkins> Janina: previous discussions entangled benefits of various ways to handle it, without solving things
<TabAtkins> rachelandrew: I think so, it keeps things in CSS
<TabAtkins> rachelandrew: With flex and grid layout you *know* you're changing the order. Layout itself can change it. So you know you're doing it, and can trigger reading-order-items
<matatk> q?
<TabAtkins> matatk: Did you say default is DOM order, that's what normal does?
<TabAtkins> rachelandrew: yes
<TabAtkins> matatk: There's usually an auto value, why not here?
<TabAtkins> astearns: One reason for normal vs auto is normal often has a single default behavior, while auto responds to various things
<TabAtkins> astearns: So someone might interpret auto to mean "if I have a grid, do grid order"
<astearns> ack TabAtkins
<emeyer> TabAtkins: I think this is fine but there’s a necessary followup issue
<emeyer> …We resolved that if you display: contents an A, the things inside A should still be tabbable
<emeyer> …That interacts badly with the non-normal values here
<emeyer> …We need to define something to fix that
<emeyer> …I suggest the right answer is it should be put after either first or last in DOM order
<emeyer> …If we need something more later, we could add integer back in
<ydaniv> q+
<TabAtkins> matatk: I think with any of these technical things, Paul is our css expert. Paul hasn't commented on this issue.
<TabAtkins> matatk: I'd feel better getting his opinion on it
<TabAtkins> matatk: But the general principle feels good - the author can choose a sensible layout
<astearns> ack ydaniv
<TabAtkins> matatk: And we definitely don't encourage people to use positive tabindex values - we all know why - so this sounds good in general
<TabAtkins> ydaniv: I'm a bit confused about the syntax
<TabAtkins> ydaniv: If you end up with a grid layout, and the dev specifies flex, what would that do? would we want 'auto' instead?
<TabAtkins> rachelandrew: I think it just wouldn't apply?
<TabAtkins> TabAtkins: Yeah I think it would just act like normal
<TabAtkins> astearns: So all the non-normal values would just not apply if they're not in the right layout context
<TabAtkins> rachelandrew: Similar to alignment, yeah
<TabAtkins> ydaniv: But doesn't the element already know the context?
<jensimmons> There's a good question there, though. Why do `grid`, `flex`, etc, instead of having values like `layout` — where the browser knows whether it should be grid layout or flex layout.
<TabAtkins> astearns: Rachel can elaborate, but I think the idea is that normal's the default, but it's not always the case that the visual reordering in a grid layout is appropraite for the a11y tree
<TabAtkins> astearns: So there are cases where grid layout should not be the reading order
<emilio> q+
<TabAtkins> rachelandrew: I think the intentionality is really specific to this, you
<emeyer> TabAtkins: The m ore targeted answer is that we don’t have one correct behavior for flex, or for grid
<emeyer> …I don’t think we can generally assume what the values imply
<astearns> ack emeyer
<emeyer> …Either of the cases for grid are common enough, and the results of getting it wrong bad enough, should require authors to say what they want
<astearns> ack emilio
<TabAtkins> emilio: The idea is that this modifies taborder
<TabAtkins> emeyer: Do we ahve use-cases for a11y reorder, but not taborder?
<emeyer> s/emeyer/emilio/
<TabAtkins> fantasai: I don't think so, I think we resolve a while ago that reading order and tab order should be in sync
<TabAtkins> rachelandrew: i haven't been able to come up with any use-cases either
<astearns> ack fantasai
<TabAtkins> fantasai: I think there's a little bit more going on here.
<TabAtkins> fantasai: Multiple ways to cause reordering to happen in grid
<TabAtkins> fantasai: grid-auto-flow which controls how auto-placed items get palced (row or column, and probably reversible in the future)
<TabAtkins> fantasai: Also the 'order' property
<TabAtkins> fantasai: And lastly there's the visual/coordinate order with explicit placement
<TabAtkins> fantasai: I think it's quite reasonable you'll have a grid where you are mixing some of these mechanism, some auto-placed items and some explicitly placed
<TabAtkins> fantasai: And depending on how you're performing these, you may or may not want to follow the "visual order" - the writing-mode order I'll say, ltr, ttb for english
<TabAtkins> fantasai: So there can be a mix of things that can happen and i think we're maybe not addressing what happens in these complex bits
<TabAtkins> fantasai: Might want to follow auto-flow but also 'order' is purely a visual effect
<TabAtkins> rachelandrew: I feel like if we came across those cases where author needs to be able to specify all of them
<TabAtkins> rachelandrew: I don't know if integer would solve those cases anyway
<TabAtkins> rachelandrew: And generally I only see people doing that when they've made a mistake
<florian> q+
<TabAtkins> rachelandrew: So I think we might bea ble to find edge cases where it doesn't support
<TabAtkins> rachelandrew: But if we find them and decide we need to fix them, we're not blocked from fixing them
<TabAtkins> rachelandrew: But the stuff I'm being asked about by devs is very simple
<TabAtkins> rachelandrew: They just want reading order "correct" so they can swap placement between mobile and desktop
<TabAtkins> rachelandrew: And what we have here will enable those. We can address more cases as we realize we need it.
<astearns> ack florian
<TabAtkins> I think th ecurrent grammar is plenty extnesible, too
<TabAtkins> florian: I think the high level takeaway is we're giving a switch telling the browser what mode it's in to traverse
<TabAtkins> florian: For typical patterns
<TabAtkins> florian: If we find more atypical patterns we can do an escape, but we're not there yet
<TabAtkins> florian: Speculating on the atypical patterns today won't be too instructive
<TabAtkins> Janina: I find this encouragin, we've been wrestling with it for a while
<TabAtkins> astearns: So proposed resolution is we drop reading-order:<integer> and go with reading-order-items with the values proposed in TAb's comment (for now)
<TabAtkins> fantasai: I think it's fine to draft it up
<TabAtkins> fantasai: I don't know I'm confident this is definitely the way to go
<TabAtkins> fantasai: So let's draft it into the ED, I'm just not 100% it's how we want to finish
<TabAtkins> fantasai: I don't like it to imply that this is what we're going for definitely
<TabAtkins> rachelandrew: I think we do need something firm to start moving forward on
<fantasai> proposal: Draft reading-order-items into the ED for css-display-5
<TabAtkins> rachelandrew: We're not blocking the integer method
<TabAtkins> astearns: and drop reading-order:<integer> for now?
<TabAtkins> fantasai: I guess so
<TabAtkins> astearns: So proposed resolution is to add reading-order-items into the ED and remove reading-order:<integer>
<TabAtkins> astearns: Objections?
<TabAtkins> RESOLVED: add reading-order-items into the ED and remove reading-order:<integer>

Draft is now in the ED, and other comments have been captured as new issues.