openui / open-ui

Maintain an open standard for UI and promote its adherence and adoption.
https://open-ui.org
Other
3.59k stars 191 forks source link

[exclusive accordion] developer poll on behavior of exclusive accordion #812

Closed dbaron closed 1 year ago

dbaron commented 1 year ago

This poll was previously discussed in #786, particularly starting from https://github.com/openui/open-ui/issues/786#issuecomment-1681218497 . However, I'm putting the poll itself in a separate issue.


There is a proposal to add a feature to the <details> element in HTML by adding a name attribute to the <details> element. This feature would allow a set of <details> elements to be related to each other because they have the same name. (This is similar to <input type=radio> elements being related based on their name.) The idea of this feature is that it allows multiple <details> elements to be linked in an "exclusive accordion", where opening one of the elements closes the currently-open one. For example, this creates a group using the name options:

<details name=options open>
  <summary>Size</summary>
  ...
</details>
<details name=options>
  <summary>Color</summary>
  ...
</details>
<details name=options>
  <summary>Material</summary>
  ...
</details>

The idea of this feature is that an exclusive accordion always has either zero or one open <details> elements.

In this proposal, the browser helps to enforce this exclusivity idea. If a user clicks on a <summary> to open a <details> element, any other already-open <details> element in the group is closed. The same happens if a web page uses setAttribute to change the open attribute or sets the open property on the element.

Another result of the idea of the feature is that it is considered an authoring error to write HTML like this:

<details name=options open>...</details>
<details name=options open>...</details>
<details name=options>...</details>

because two of the <details> elements are initially open, within a set that is intended to have at most one open. An HTML validator should report this as an error.

Because the <details> element maintains its open/closed state as an attribute, there are cases where it's not reasonable for the browser to immediately enforce the idea that only one <details> element in the group is open. The simplest example of such a case is the "authoring error" case above. This is also the case when <details> elements are added or moved in the DOM by using features like innerHTML or appendChild. (There are also other related cases like handling of changes to the name attribute.)

We'd like to know what developers think should happen in these cases (such as when the HTML parser encounters the example above that involves open attributes on two different elements). The two main options for handling this are:

  1. The elements remain open (as specified in the markup) until a different <details> element in the group is opened, which causes all the open ones to close. This means that the "exclusivity" concept is broken until either the user or a script opens a different member of the group.
  2. The browser closes all but one of the open details elements (leaving either the first or the last open) in a task that happens slightly after the insertion. This means that the state of having multiple <details> elements open will be visible to script for a period of time, but will get corrected before the document is visually displayed to the user. This also means that the specification will need to say that the browser picks either the first or last of the open <details> elements as the one that was intended to be open.

What do you think about these options?

Please vote with a single emoji reaction to this comment. (Or, if needed, add a comment to the issue explaining some other option or add a 👍🏼 to somebody else's comment.)

Schepp commented 1 year ago

I tend to option 2, but I'd like the browser to render the first <details> element open that it comes across having that attribute and to ignore any further open ones it finds later. That would circumvent the browser needing to correct things after the fact and would work in line with autofocus, but opposite to <input type="radio">.

meyerweb commented 1 year ago

This is difficult, because I believe option 1 should be an option for authors. I just don’t know that I think it should be the default behavior. The problem is, the obvious way to invoke that state (given this markup pattern) is to have multiple open attributes, which is also the authoring-error state, and I don’t have an obvious solution (given this markup pattern) that would allow multiple open states while having a different default behavior.

As I typed this out, I realized I would have liked be an option for “this markup pattern seems insufficiently expressive for the stated goal(s) and should be reconsidered or replaced with something else”. Thumbs down, I guess?

callionica commented 1 year ago

You don’t say why “ it's not reasonable for the browser to immediately enforce the idea that only one details> element in the group is open”. The browser could immediately enforce this state, couldn’t it? I haven’t been able to vote because there’s an assumption behind the question that I’m not getting yet.

brucelawson commented 1 year ago

😁 - I think it's too weird for the browser to close some details after page load with no user initiation.

dbaron commented 1 year ago

A few responses to the comments above:

@Schepp Agree that if we go with option 2 we probably want first rather than last to win, because of the progressive rendering effects.

@callionica There's a good bit of discussion of that in #786.

@brucelawson The closing in option 2 wouldn't be "after page load" -- it would be between parsing and rendering, so wouldn't be visually distinguishable from the closing happening immediately, but could be distinguishable by script that runs at the right time.

albertsemple commented 1 year ago

1.

The details collection needs a defaultDetailOpenAction CSS attribute with values of "exclusive" and "non-exclusive".

When a details element opened, if the default action is exclusive, close all the others .

If it's non exclusive, leave them alone and open the new one too.

The same logic should apply when a new details element is added to the collection if new element is set to open. If it's inserted closed, no logic to run.

MaxArt2501 commented 1 year ago

We can take inspiration from radio buttons: inserting new pre-checked buttons does not break the consistency. The same should happen here, so only one element should remain open. Whether it's the first or the last (as for radios) find me quite neutral. I'm not sure about the delay between appending the new elements and fixing the exclusivity. For radio buttons, it happens synchronously, and I'd expect to see that in this case too. What's the rationale for an synchronously adjustment? Would it be performed in the next microtask? Or the next event loop iteration? Next frame, maybe?

nevali commented 1 year ago

i strongly prefer the effect of 2 but not the proposed mechanism, it feels complicated as compared to simply "once one open details element has been encountered, subsequent open attributes in the same group are ignored" or "each successive details element with an open attribute takes precedence over any previously-encountered open details elements (and so when a later one is added to the DOM, the open attribute is removed from whichever one is currently marked open, and that will keep happening each time another <details name="…" open>) is encountered in the source)"

~what's the expected behaviour if you add a new details element that has the open attribute and named as part of a group that already has an open details element to the DOM after page-load? i.e., the DOM has an exclusive-accordion, one of its details elements are open, and you add another in a script — it seems like the answer to that is probably the answer to this~

[Edit: sorry, just spotted that was in fact part of the question, but it's maybe more helpful to think of it in terms than about markup and parsing necessarily just because it seems much more likely to happen via script]

it is considered an authoring error to write HTML like this

okay, so secret but probably unpopular (for good reasons, not least the question above) third option: specify the whole thing as an error in markup (as written) and that it therefore is in the realms of undefined behaviour, and renderers can do whatever they like and so whether you get option 1 or 2 is entirely at the engine developers' discretion

Schepp commented 1 year ago

and renderers can do whatever they like and so whether you get option 1 or 2 is entirely at the engine developers' discretion

That would bring us back to the ~good~ bad old times from before HTML5 where each engine's parser would resolve invalid markup to whatever it wanted as it was not defined by the spec. So I'd prefer that not to be the case. 😅

bradkemper commented 1 year ago

I prefer #2 if it follows the same rules as option buttons, which I believe is "last one wins", isn't it? If the fix-up happens before the DOM ready event, it should be fine for scripts to deal with.

bkardell commented 1 year ago

I prefer #2 if it follows the same rules as option buttons, which I believe is "last one wins", isn't it? If the fix-up happens before the DOM ready event, it should be fine for scripts to deal with.

It doesn't only happen during parsing. If you build a one and insert it into the tree, same thing. If you change the name to create that situation, same thing.

romainmenke commented 1 year ago

In option 1 users will first encounter a state that they won't be able to recreate. They will see a number of open detail elements which will all close when they open another in the same group. Users will not be able to go back to that initial state.

I think that this is unexpected and can confuse people. Why were those panels open together, was this meaningful? Why can't I see these open together anymore?

(and user needs come before author needs)

YummyBacon5 commented 1 year ago

@romainmenke I think the intent of option 1 is for authors to catch that there is an error easier.

But that is a good point. And what if authors want to replicate this errored state again, after user interaction?

And now that I think of it, option 2 could have a warning in the console to let authors know that there is an error. Although, I think that this is unusual.

dginev commented 1 year ago

A bit of a curious aside: Has the paradox of "This sentence is false" been guarded against already?

Namely:

<details name="nested">
    <summary>Life is</summary>
    <details name="nested" open>
       <summary>hard</summary>
       <!-- this could go on even deeper -->
    </details>
</details>
bradkemper commented 1 year ago

It doesn't only happen during parsing. If you build a one and insert it into the tree, same thing. If you change the name to create that situation, same thing.

@bkardell would the "having multiple <details> elements open will be visible to script for a period of time" thing would be hard to work around in that situation? I kind of think if you were inserting one into a tree, you could check the open/closed state of the existing <details> elements before inserting, so this shouldn't be a problem either. Unless I'm missing something.

johanhalse commented 1 year ago

I would prefer option 1 for debuggability. Unlike radio buttons we don’t have to indicate any “truth” being sent to a form target, so we might as well display what the author has written. I’ve lost hours over the years head-scratching over wrongly-rendered radio buttons and would not enjoy doing the same over details elements…

Schepp commented 1 year ago

<summary>Life is</summary>
<details name="nested" open>
<summary>hard</summary>
<!-- this could go on even deeper -->
</details>
</details>```

That's indeed an important consideration!

Seems as if this needs an orchestrating parent after all, which might be called <accordion> or <detailsgroup> so that then you could constrain the grouping of <details> elements to only its direct (or closest) children. No need for a name attribute anymore to link the individual members of an accordion group.

The one feature you'd loose is being able to scatter group members wildly across the document, but what real use case would that be?

albertsemple commented 1 year ago

A generic container with exclusivity set as a CSS property.

For accordion behaviour, set exclusivity to true.

Would make it easy to programmatically change the CSS if there was a need to switch between seeing one detail or multiple - I.e. recreating the error state.

Feels more semantic than an element or a name= attribute, although would make it inconsistent with the exclusivity implemented for radio buttons.

Has anyone ever (intentionally) used radio button exclusivity where the buttons couldn't be contained in a single parent - i.e. scattering the radio buttons? I can't think of any instances. This proposal would be deviating from the pattern established for radio button exclusivity, but that pattern may be flawed to start with.

Westbrook commented 1 year ago

Option 1 means that the developer is respected until the visitor is, which feels like an important reality to support here. Multiple open items is a fail state after interaction, but in the initial HTML it’s easy to see that as intended.

LJWatson commented 1 year ago

Option 2, and with it being the first <details> with the open attribute that is rendered in the open state.

aardrian commented 1 year ago

Another use case besides author error or script insertion is find-in-page. In discussing this, I found a couple devs were unaware that Ctrl + F in Chrome opens a native <details> when a term in a collapsed <details> matches. It gave them pause but did not change their vote (but did make them reconsider <details> overall for their scenario). No idea if that would impact others' votes.

aardrian commented 1 year ago

Way out of scope of the survey, I know. Sorry-ish.

@Schepp

Seems as if this needs an orchestrating parent after all, which might be called <accordion> or <detailsgroup> so that then you could constrain the grouping of <details> elements to only its direct (or closest) children. No need for a name attribute anymore to link the individual members of an accordion group.

If that happens, it can lean on group role and/or landmark semantics, which already exist in browsers and are exposed via Accessibility APIs. Essentially converting the following construct into a dedicated HTML element:

<section role="group" aria-labelledby="accName">
  <details>[…]</details>
  <details>[…]</details>
  <details>[…]</details>
</section>

The one feature you'd loose is being able to scatter group members wildly across the document, but what real use case would that be?

I struggle to think of one that does not risk 1.3.2 Meaningful Sequence or 2.4.3 Focus Order WCAG violations. Maybe let's not build that in.

chipcullen commented 1 year ago

I'm more in favor of Option 1 for the reasons stated:

This is difficult, because I believe option 1 should be an option for authors.

Option 1 means that the developer is respected until the visitor is, which feels like an important reality to support here.

I would prefer option 1 for debuggability.

I personally feel like Option 2 would cross a boundary into a browser trying to infer the author's intent in a way that might not be accurate. And it would be hard for an author to figure out why, without digging into HTML documentation somewhere. Whereas with Option 1, if two (or more) details are open, it's immediately clear why, and easier to reason about.

serebit commented 1 year ago

I'm in favor of (1) simply because it correctly reflects when mistakes are made by the author of the page. (2) obscures the mistake almost immediately. I agree with @chipcullen—(1) is simply more intuitive behavior. The browser should not assume intention by the author.

Lurk commented 1 year ago

A few comments: 1) To make nested accordions foolproof, I prefer a parent tag instead of a name attribute. 2) Exclusivity should be explicitly controlled by an attribute (or CSS). 3) If, in an exclusive context, two details have an open attribute - the first wins.

petergoes commented 1 year ago

As a user, I would prefer to have control over which items expand / collapse. I often want to compare two sentences in different details, which I need to have open at the same time. So I would not prefer this behaviour.

As an author, this comment feels most intuitive

Seems as if this needs an orchestrating parent after all, which might be called <accordion> or <detailsgroup> so that then you could constrain the grouping of <details> elements to only its direct (or closest) children. No need for a name attribute anymore to link the individual members of an accordion group.

The name attribute feels magic, a parent element feels more readable to me. Besides, that leaves the option to configure behaviour further. The parent element could have attributes that control the max amount of open details, for example.

Handling more open details than the specified max, I would do via option 2. The parent element could also specify if the first, or last items remain open after exceeding the max amount.

bkardell commented 1 year ago

I'm hesitant to do anything that might seem like a kind of advocacy here, but I'm also slightly curious if people who are adding comments to specifically that say #1 is more intuitive are basing that solely about initial parse case. For example: If you look at this codepen (with the experiment in chrome enabled) as one example, is that what you would intuit? That this action would not close others (and would itself open too)? There are several ways to get multiple open... Did you mean to say "that's all intuitive" or just the initial parse case?

@serebit @chipcullen @Westbrook

For posterity tho I am going to embed the state of the poll before I said this - just incase someone thinks this comment poisons results that follow.

Screenshot 2023-08-29 at 3 26 33 PM
mems commented 1 year ago

I see the exclusitivity of accordion as the same as a select and its options. The value always match the latest option selected, and change immediatly.

The option property selected getter always reflect the state (the selectedness), where the attribute selected doesn't always reflect it state ("represents the default selectedness").

See the selectedness setting algorithm, option insertion and removing steps and ask for a reset.

serebit commented 1 year ago

@bkardell I was only thinking of the initial parse case, but after trying out your codepen, I think I agree that #2 is better. Even if it obscures a mistake, the browser should indeed attempt to maintain consistency. Changing my vote to ❤️.

albertsemple commented 1 year ago

I'm also slightly curious if people who are adding comments to specifically that say #1 is more intuitive are basing that solely about initial parse case.

Reminder re option 1

1 - The elements remain open (as specified in the markup) until a different details element in the group is opened, which causes all the open ones to close. This means that the "exclusivity" concept is broken until either the user or a script opens a different member of the group.

I think this remains valid, but the "opens a different member of the group" event should include trapping the insertion of a new open member.

It would therefore be possible the break exclusivity again by inserting multiple open members in a single operation, but insert one open element would be covered by the "opens a different member of the group" logic.

Westbrook commented 1 year ago

Even with the example I still go with no. 1. Adding an open member to the group is NOT the same as opening an existing member of the group.

What I expect if no. 1 isn't the option is that you'd get developers copying:

<details name="one" open>
  <summary>A</summary>
  This is A
</details>

From somewhere and the copying:

<details name="one" open>
  <summary>B</summary>
  This is B
</details>

From somewhere else, and then being quite confused as to why each is not open. It'd be even worse if they didn't expect the name to associate the two and were even more confused.

In the case of no. 2, the precedent for collapsing multiple values into one seems to be set with <select> where the last of any selected <option> children becomes the value. Would last winning here too be sensible? Would switching to first make for a confusing collection of lumpy standards?

ericwbailey commented 1 year ago

No opinion, just wanted to re-stress SC 1.3.2 Meaningful Sequence and 2.4.3 Focus Order concerns as steering thoughts.

bkardell commented 1 year ago

It would therefore be possible the break exclusivity again by inserting multiple open members in a single operation, but insert one open element would be covered by the "opens a different member of the group" logic.

@albertsemple I'm sorry, I'm still not sure we're on the same page. If this means that you would expect the codepen to close the others on insert because it is inserting only 1 --- that is not option #1 (in fact, it's part of the reason we're polling). Option #1 would currently leave both open (as would some other things).

jimmyfrasche commented 1 year ago

Stray thought: what if you make it generally exclusive but allow multiple summaries to be open. Specifically, clicking one closes the others but shift or ctrl or whatever clicking it opened it without closing the others. Then it gives users more control and turns the edge cases above into normal cases.

bkardell commented 1 year ago

@jimmyfrasche I don't believe that this is a feature of any of the systems surveyed in the accordion research, and I don't believe that I have ever seen that pattern either. While it seems semi-interesting, it raises several questions for me.. Can you point to one or more existing/hopefully popular examples that do this?

jimmyfrasche commented 1 year ago

No, but it contains the desired pattern while avoiding the hard problems. I wish more accordions did this because they make it very difficult to compare text in two different panels which I have had to do before as a user more than once and I usually just open up dev tools and force them open. The most exciting thing to me about this proposal was that I could write a bookmarklet to remove the name attributes when I had to do such a comparison but being able to middle click would be even simpler.

kytta commented 1 year ago

I think people who choose Option 1 do this not because it's a better option, but because they're used to seeing it. For me, a self-fixing accordion indicates incorrectly written JavaScript, where the check for exclusivity fires after a click event or something.

For me, an exclusive accordion represents a book on a lectern or a binder cabinet: If you want to look at a page, you need to hide the page you're looking at. No two pages can be displayed at the same time, so it makes no sense that it would work on the Web.

As such, I am all for Option 2. However, I do believe that the last element should be the one that's open, which can be explained in multiple ways:

  1. This is how radio buttons do it, so let's just reuse the behaviour we already have in HTML
  2. It's not the only place where the latest element is favoured; I could also compare it with CSS, where it's the latest mention of the selector that has the final word

Lastly, one could also think of HTML parsing as an imperative process. <details name="a" open>...</details><details name="a" open>...</details> is going to be read by the browser as follows:

As such, the latest state of the page corresponds to the latest <details> being open. For me, this makes total sense and is the most expected outcome.

Westbrook commented 1 year ago

Keeping first or last brings another interesting point of possible confusion if option 2 is used.

When adding a new opened item to the end of the list, what happens?

What gets more confusing is that whichever of these might end up being true, you get the opposite interaction when adding an open item to the beginning of the list.

The user stories, spec text, and documentation of option 1 are both so much less problematic than those of option 2:

bkardell commented 1 year ago

@Westbrook can you include some snippets or links that illustrate what you mean? It seems to me we might have some confusing uses of "first" or "last" with respect to whether that is "in time" or in others "in tree order". During parse or insertion of fragments with multiple there is overlap, but, for example, whichever <option selected> is inserted last would be selected, regardless of its child index position - right? See https://codepen.io/briankardell/pen/vYvKLLX

Westbrook commented 1 year ago

Does "last" mean "last in time" here? That would be one piece I might have missed and/or an important inclusion in the spec. That would reduce, though not fully resolve, possible confusion in this area.

For example, in a visual context...

Delivering this code:

<details name="one" open>
  <summary>A</summary>
  This is A
</details>
<details name="one" open>
  <summary>B</summary>
  This is B
</details>

Would leave B open.

While delivering this code:

<details name="one" open>
  <summary>B</summary>
  This is B
</details>
<script>
  const b = document.querySelector('details');
  b.insertAdjacentHTML('beforestart',
`<details name="one" open>
  <summary>A</summary>
  This is A
</details>`);
</script>

Would leave A open.

This feels different and less expected than what you get in <select> for the fact that it is immediately visible and the idea that multiple in that context is the opt-in feature rather than the opt-out feature, as we see in this element.

bkardell commented 1 year ago

Does "last" mean "last in time" here?

It does in my words, though obviously there is correlation. When the document is received, the ones later in the tree are inserted later in time.

But yes both cases you've said above are what feel intuitive and actually respecting what I feel I've said as a developer. I guess it seems there is not even remotely perfect agreement though (unsurprising, in fact even in terms of these kinds of polls this is lopsided because normally it is almost exactly 50/50 :)).

bradkemper commented 1 year ago

Last in index order seems more intuitive to me than last in time. But how is it with radio buttons or select options? I think we should be consistent with those, as much as possible.

ZoeBijl commented 1 year ago

Can I record a vote against exclusive accordions? They’re such a frustrating UI pattern.

yatil commented 1 year ago

I agree with @ZoeBijl here. Exclusive accordions are largely an antipattern that should not be used. If implemented, I would expect browsers to allow users to opt out of the exclusive behavior per site and generally, similar to how autoplay can be ignored by users.

That said, I think it would be good if there was a native way to build these features as there is a demand for this antipattern and when it is easy to bypass, it would increase accessibility as well as making it easy for developers to implement features requested by business or design in an accessible way.

brechtDR commented 1 year ago

The reason why i chose option 1 is the following:

I think the first idea is that the items should both be open as this makes sense from a visual standpoint.

It could help to have all the items with an open attribute visually open at first load as a screen reader technology might pronounce them both as open? Or am i thinking about this the wrong way?

Because screen readers can also be used by people who actually have visual abilities (dyslexia for example can be a use case) It seems normal that when something has the open attribute, it is also shown open.

Unless i get it wrong and we let the browser actually remove the unused attribute in option number 2

YummyBacon5 commented 1 year ago

This should be closed now. Since there is a resolution to choose option 2. And that #786 has been closed aswell.