w3c / csswg-drafts

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

[css-anchor-position-1][css-display-4] Anchor Positioning and Display Order #9356

Open kizu opened 1 year ago

kizu commented 1 year ago

I was thinking a lot about how anchor positioning can be applied for popovers and tooltips, as well as other cases like sidenotes .

In a lot of these cases, the popover/sidenote/other content can be positioned absolutely in relation to some anchor, so the sighted user would get an understanding of how these elements are connected.

However, semantically, there is nothing that connects the anchor and the content associated with it, and the reading and tab order would not be correct, following the order in the DOM.

This issue is to discuss if we can somehow approach it and what could be the best way to do it. If there was a different issue that was already discussed, let me know!

Right away, I want to list other places where I found people mentioning the need to cover this aspect of absolute positioning:

My Proposal

Now that we are thinking about providing a way to modify the reading order for flex and grid items via something like reading-order-items (https://github.com/w3c/csswg-drafts/issues/8589), what if we would also make a similar method apply for anchor positioning?

My idea is to make the anchor defined by anchor-default (and, thus, the implicit anchor defined in HTML via an anchor prop) the key to solving the reading/tab order for the absolutely positioned elements.

I think in almost every case, when we position something in relation to a specific anchor, we want the tab order to follow from the anchor to the positioned element and then back to the flow after the anchor.

Can we just do this by default? Do we need to make this opt-in while keeping the default DOM order, similar to how we keep the flex/grid intact? I'd argue for handling this by default and maybe providing a way to opt-out, as there is no fear of compat issues due to anchor positioning being new, and I really think almost all cases where we'd have a default anchor would want this behavior.

Examples

I went through the examples presented at TPAC (got the link from these minutes) and separated all the examples into three groups:

Purely visual

Some cases for anchor positioning can be used for purely visual purposes, like most of the examples in my article, or the positioning of the ::backdrop with an outline to create a “gap” in it.

I don't think there are any reservations for these cases — usually purely visual elements won't have any content to read or tab onto, so the behavior of anchor-default won't matter for these.

Popovers and Tooltips

In most cases, a popover would appear after a click on some button, and a tooltip would appear after hovering or focusing over some element.

A screenshot of a tooltip/popover from an editable text toolbar, allowing to choose the color to highlight the text with.

Whenever this happens and a popover/tooltip appears, from a keyboard standpoint, it would be logical for the next tab to land inside the next interactive element inside the popover/tooltip. Same for the reading order: I think it would be logical for it to follow into the popover as if it were placed in the DOM after the anchor element.

Main challenge: how to handle dynamic display and position of the absolutely positioned elements?

We would need to properly handle the case when we're on an element inside that popover, then we dismiss it in some way (from the keyboard or by pressing on the backdrop) — the popover closes either by being removed from the DOM, or by getting display: none, or maybe even losing the className that gave it the position: absolute, but keeping it in the flow (I can imagine footnotes working this way: we could yoink a footnote and show it as a popover, and put it back when dismissing).

The expected way we'd want this to be handled is for the “focus” to return to the anchor element, even if the association would be lost dynamically (anchor-default reassigned/unset).

Sidenotes

I think this example from @meyerweb's recent article is a good one to look at:

A sidenote next to the main text column, with its number aligned with the referencing number found in the main text column. There is a link present in the sidenote. The sidenote is tightly associated with its reference, and there might be tabbable content inside the sidenote that we would want to access from the keyboard right away.

I think it would be completely logical for the tab order to follow the sidenote.

Reading order (and the whole semantics of the sidenotes — are they aside, are they a description?) is more uncertain. But as a sighted user, I find that I almost always go right away inside the sidenotes/footnotes, breaking the flow.

I cannot speak for screen reader users, but I imagine it is better to also get the sidenote content immediately after the sidenote in some way, given there would be some way to skip it. People who are more knowledgeable in this matter could weigh in on it — it would be very helpful!

Edit from 2023-09-16: Another aspect why making the sidenote follow its context immediately could be that for the sidenotes they often might not sense without their context. If a paragraph would contain 3 sidenotes, then you'd get read all of them in a row after the paragraph, you'd need to somehow remember which would apply to which place, or have some way to jump between them and the context? In any case, even if not skippable, I suspect the experience of having the sidenote content right after their context much easier to understand, though there really should be some way to be able to skip them — I'm not sure what could help with this as I'm not a screen reader user. Making the sidenote a description? Something else?

However, this might be a case where the out-out for the following the anchor-default could be helpful, as it would allow authors to more precisely control how they should such elements behave. Though, we could argue, that we could still utilize the anchor-default, and place the default anchor onto the paragraph in question (while utilizing a custom named anchor for the actual anchor), making it so the reading and tab order would follow after the paragraph.

Edge Cases

Multiple Positioned Elements

Multiple elements can target the same anchor. I'd say that if all of them using the anchor-default, the reading/tab should follow the anchor through all these elements in their DOM order, then back to after the anchor.

Conclusion

I think this is an important issue to think about now, so we won't create a case where anchor positioning would ship with subpar accessibility, making it harder to do things right afterward without breaking compat.

I am not an accessibility expert, so I could be incorrect in a lot of my assumptions, and I don't know if that behavior I'm talking about is easily implementable or has other issues to think about — that's why I invite anyone to comment, provide their use cases, and so on.


cc @tabatkins, @fantasai, @jensimmons

AutoSponge commented 9 months ago

I think it should be opt-in because I can imagine situations with something like a tooltip that appears in different orientations to the content/context element and therefore the reading order would vary depending on the instance possibly causing confusion.

kizu commented 9 months ago

I think it should be safe to assume that the tooltip's content always follows the context's content in the reading and tabbing order regardless of the tooltip orientation. I can't think of any real use case when someone will want otherwise.

AutoSponge commented 9 months ago

Password hints (instructions) could be read before an input with better effect than following the input.

kizu commented 9 months ago

Using passwords for these kinds of hints will be bad UX for everyone tbh, not sure if it is worth breaking the experience of all other tooltips and popovers just for this use case.

With a way to opt-out, edge-cases like this one could be covered by the developers, but I think the goal should be to make the prevalent number of the most common use cases better, no?

AutoSponge commented 9 months ago

I don't think we're talking about the same use case. But I digress. This is a problem developed by modern framework developers who didn't put elements in the "correct" document order. Making this a default behavior seems like an overreach regardless of any contrived scenarios where it helps or hurts the experience because that's often too subjective.

tabatkins commented 8 months ago

I think that the default behavior definitely needs to not change the tab ordering at all; the tooltip use-case probably wants it, but other use-cases (even using anchor-default) don't necessarily. For example, if you're just anchoring to some container, you probably don't want to be right after that container in the tab order (or at least, there's definitely enough doubt about your intent that we shouldn't change things by default).

There's also other focus-management work being done in HTML that I'd want to at least be consistent with (tho I'm not sure what the status of that work is now).

As such, I'd like to defer this to level 2. It would be great to have this work as easily as possible, but there's enough questions to answer that I don't want to make level 1 depend on it, and it will be safe to add in the future.

(Once Bikeshed switches to using anchor positioning for its link/dfn popups rather than JS-computed positions, it'll still need to keep its tabindex shenanigans, so it would be great if anchor positioning could somehow solve this!)

mfreed7 commented 8 months ago

As such, I'd like to defer this to level 2. It would be great to have this work as easily as possible, but there's enough questions to answer that I don't want to make level 1 depend on it, and it will be safe to add in the future.

It seems to me that most of the example use cases where you do want to do "focus order fixup" are related to Popover use cases. That is mentioned in the OP here as well. It's important to point out that the Popover API itself provides exactly this focus fixup behavior, automatically. A popover automatically comes immediately after the element that invoked it in the tab order, regardless of where in the DOM that popover resides. That's spec'd here:

So I agree: it seems like a more general feature related only/specifically to anchor positioning could be deferred to level 2.

cookiecrook commented 8 months ago

A few related items to consider...

AT activation of the link the might be one way to determine whether it's relevant to render this subsequent content in linear reading order. click the link, read the footnote, continue with the sentence.... or if I don't click the link, just continue with the sentence. (ack: my example is somewhat specific to the footnote/glossary case.)

[Update Apr 18: the second bullet reference to Aaron's feature pitches included ARIA Annotations, which landed as part of aria-details, and a repeated content concept for page headers and footers. I don't think any of these related topics block the discussion of anchor positioning and display order.]

tabatkins commented 7 months ago

@cookiecrook Would you agree, then, that this is (a) probably not something we should be doing in all cases (automatically reshuffling focus order so a positioned element's focusable elements go immediately after its default anchor element), (b) something still under discussion in general, so nailing the details now isn't feasible, and (c) something we can switch on in the future in some way?

cookiecrook commented 6 months ago

Sorry for the delay. I would've sworn I'd responded already. (Must've dismissed a draft.)

I don't think any of your options as phrased (a, b, or c) represent my opinion on the matter. I do think this concept is worth discussing now. I

(a) probably not something we should be doing in all cases (automatically reshuffling focus order so a positioned element's focusable elements go immediately after its default anchor element),

Your phrasing here may be too hypothetical to confirm or deny. Are there specific scenarios you are referring to? I gave some in the comment above to further spark discussion.

(b) something still under discussion in general, so nailing the details now isn't feasible, and

Under discussion doesn't negate feasibility IMO.

(c) something we can switch on in the future in some way?

I would advise against punting the problem to a future date.

tabatkins commented 6 months ago

Hm, okay, then let's step back and rephrase a little.

So, Anchor Positioning is a pretty generic layout feature (an elaboration on the existing position: absolute;) that lets an element set its position relative to one or more other elements on the page. The common use-case is something like a popup, where the positioned element is being placed adjacent to one element in the page. But there is a wide variety of other things you can achieve with this functionality, where the tab-order relationship between the positioned element and the anchors is either unclear, or there is in fact no such relationship. For example, you can use a container element as an anchor, and position yourself in one of the corners - this is indistingishable, stylistically, from the popup case, but almost certainly doesn't want the popup to have some particular tab-order relationship with the container.

Further, for that common use-case I mentioned, the Popover API is intended to capture most such examples; further elaborations like the interesttarget proposal do even more. Using Popover or its sub-features does clearly establish a semantic link between the invoker and the popup, which we do rely on for tab-order fixup - that's already built into the feature.

You mention a few other features, like ARIA Annotations, which similarly might use anchor positioning to style themselves, but which don't rely on the anchor positioning to establish the semantic link between the two elements; instead, it's established by the other feature.

So, since the generic CSS behavior can do a whole lot of things, and doesn't consistently establish a clear semantic relationship, I'm arguing that we do not want to attempt to infer any such relationship from the styles and reshuffle tab-order accordingly. Instead, we want to rely on more targeted features like Popover, ARIA Annotations, etc. to establish such linking, and those features will usually simply use anchor positioning for their own styling.

Does this sound reasonable? Or do you have other examples you're thinking of where a semantic relationship would be conveyed solely (and ideally reliably) by the presence of some detectable pattern of Anchor Positioning usage, and there's no other, more reliable/preferable way to establish that relationship (in markup, script, or otherwise)?

cookiecrook commented 6 months ago

Thanks for the additional explanation.

While I agree that anchor positioning by itself is not sufficient to establish the type of semantic relationship between elements, the proximity-based rendering often (frequently?) would convey a navigational relationship... In particular for the reading order (including tab loop if elements are focusable), a sighted user could infer a logic to the reading order based on the anchored position relationship... And I think that's the intention of @kizu's proposal.

I think in almost every case, when we position something in relation to a specific anchor, we want the tab order to follow from the anchor to the positioned element and then back to the flow after the anchor.

I'm sure we could come up with a hypothetical example that doesn't fit this, but Roman's suggestion does make sense to me as a default or option.

tabatkins commented 6 months ago

As a counter-example, and a demonstration of how we can't reasoanbly infer these relationship, consider a big grid of data, and the sorting controls are a positioned element anchored to it, just outside the grid and flush against its top edge

.data-table {
  anchor-name: --data-table;
}
.sort-controls {
  position: fixed;
  position-anchor: --data-table;
  inset-area: top span-right; /* outside the grid, this bottom edge against the grid's top edge */
  justify-self: start; /* left edge aligned with the grid's left edge */
}

Here, you probably want the sort controls to show up in the tab order before the contents of the big data grid, by default.

Contrast that with this commentary case:

.item {
  anchor-name: --item;
}
.comment {
  position: fixed;
  position-anchor: --item;
  inset-area: top span-right;
  justify-self: start;
}

Here, you probably want it to read/tab the item's contents first, then go to the comment.

The styles in these cases are exactly identical, the difference is purely contextual, not expressed in the style system at all. The correct thing to do is annotate these elements appropriately: in the first case, probably just aria-controls on the positioned element, pointing to the data grid, and maybe aria-owns on the grid pointing to the controls; in the second case, aria-details on the item, pointing to the tooltip, and role=comment on the comment.

I simply don't think that either case can be reasonably inferred from the style system. I tried really hard to infer similar relationships from the style system when working on CSS Toggle States, but that also ended up being too complex and ambiguous to do anything useful. The final conclusion there was that we needed more targeted proposals that did naturally impose specific semantics - things like popover, details groups, and the in-early-development carousel-related properties.


Note that "as an option" is still on the table; what I'm pushing back against is "as a default". If we want to make tab ordering more controllable from CSS, with some explicitly-designed properties, I think that's a great idea. But it goes beyond Anchor Positioning and is instead a separate feature that could be used much more broadly.

cookiecrook commented 6 months ago

Typically I see sort controls near a specific header cell (Excel, Numbers, etc), so in that design on the pattern you mentioned, I would expect the focus order of the floating control to follow the header cell.

If you have a visual mockup in mind of the sort control outside the grid, it may be helpful to share the illustration so that we’re visualizing the same control. Thank you.

mfreed7 commented 6 months ago

Note that "as an option" is still on the table; what I'm pushing back against is "as a default". If we want to make tab ordering more controllable from CSS, with some explicitly-designed properties, I think that's a great idea. But it goes beyond Anchor Positioning and is instead a separate feature that could be used much more broadly.

I would hope that such a feature would be general, and would handle similar situations with "pure" absolute or even relative positioning. Right?

E.g.

<div style="margin-top: 2em;">and then this <button>second</button></div>
<div style="position:relative;top:-3em;">Please click this <button>first</button>,</div>

Screenshot showing "first" button displayed before "second" button

Yes that's definitely a strawperson example, but it illustrates the issue that positioning can already be used to display things out of order. So if there's a feature to fix that, it should apply to all positioning modes.

mfreed7 commented 5 months ago

https://github.com/w3c/html-aam/issues/545

css-meeting-bot commented 5 months ago

The CSS Working Group just discussed [css-anchor-position-1][css-display-4] Anchor Positioning and Display Order.

The full IRC log of that discussion <emeyer> kizu: I noticed sometimes we might want to only change behavior from one anchor element to another
<una> q+
<emeyer> …Mostly for cases like side notes and other things that are not covered by the popover API
<emeyer> …Given the API probably handles the most common cases, this may not be so pressing, but in a reading-order future, we might want to allow authors to make changes
<astearns> ack una
<TabAtkins> q+
<emeyer> una: I agree that with popovers you're covering tab-navigation as expected
<emeyer> …I agree we do need an implicit anchor relationship
<emeyer> …This feels like an HTML thing
<emeyer> …You also have other examples in that post that show the need
<emeyer> …I think an anchor attribute would be best
<astearns> ack TabAtkins
<emeyer> TabAtkins: Agreed; my major objection here is that anchoring can be used for lots of things and we can't reliably determine a relationship
<emeyer> …That's a complex and manual enough example that I don't think we can do this reliably
<emeyer> …Good announcement behavior is important
<masonf> q+
<emeyer> …The HTML AAM opened their own issue on this, and they're talking about how to bridge with us
<emeyer> …My proposal is that for the anchor positioning spec, we close with not changes but we keep working with other groups to figure out the solution, which is needed
<astearns> ack fantasai
<emeyer> fantasai: Agree with Una that this will need markup support, and it should be something in the HTML
<emeyer> …The control is going to need to invoke a bunch of accessibility bindings
<TabAtkins> And then if there's an anchor attribute, that'll establish the "implicit anchor element" for anchor post, which'll mean authors don't have to repeat themselves in CSS
<emeyer> …But I don't think ARIA is the right approach, even though we'll rely on it underneath whatever markup there is
<una> q+
<emeyer> …If we want authors to be able to use these, it should be sufficient that they'd be willing to use it instead of CSS
<astearns> ack masonf
<emeyer> …We shouldn't change things now, but I think we should keep this open until we get the information on what will be used so we can put it in the spec
<fantasai> s/sufficient/sufficiently convenient/
<emeyer> masonf: +1 to HTML markup, like popover does
<fantasai> s/change things now/change the spec normatively/
<emeyer> …Switching tab ordering between layout order and DOM order is trickier than it sounds
<emeyer> …There has been some pushback on the anchor attribute in general, so we might want to bring this up with the WHAT WG
<astearns> ack una
<fantasai> s/instead of CSS/instead of CSS. For example, in CSS we have the ability to scope patterns so that you don't have to create unique identifiers for every instance of a repeated pattern; that needs to be available in HTML also/
<emeyer> una: There's new reasoning to have the anchor attribute
<emeyer> …I really think something like popover feels like the most seamless solution
<fantasai> s/should be something in the HTML/should be something in the HTML. And also should allow anchoring either before or after the element./
<emeyer> …Handling multiple anchors like on Wikipedia is super annoying to do solely throught CSS
<astearns> ack fantasai
<emeyer> fantasai: I think the anchor attribute as currently specced is not going to be enough
<emeyer> …It only works with IDs, for example
<emeyer> …So dealing with repeated patterns on a single page doesn't work
<emeyer> …It also needs to be able to anchor an element before or after the element
<emeyer> …The current proposal doesn't hook into accessibility bindings, so it will have to do that
<emeyer> una: In the authoring process, if they have a new tooltip it will probably get a new ID, so I don't think having to do that here would be a major barrier for authors
<emeyer> fantasai: If CSS is significantly more convenient, then they'll use that
<emeyer> …Right now they do it because there are no other options
<fantasai> s/do it/use IDs/
<emeyer> …We just need to design a syntax so we can extend in that direction
<emeyer> astearns: Soundsl ike we're going to keep this issue open, but for now will pursue a markup-based solution that can hit all the issues raised here
<emeyer> astearns: Moving on…

Summary: This needs to be addressed in HTML; keeping the issue open for tracking and so that we can update the spec with appropriate guidance and cross-references.