WICG / aom

Accessibility Object Model
http://wicg.github.io/aom/
Other
572 stars 59 forks source link

Revisiting reflection #88

Open alice opened 7 years ago

alice commented 7 years ago

We did hash this out on #60, but I'd like to revisit the decision we arrived at there (sorry!).

@robdodson has been experimenting with AOM and found the lack of reflection extremely confusing, and I am still concerned that having AOM and ARIA share a vocabulary but not an API is going to be difficult for developers to understand.

Two-way reflection

As an alternative, what if we had two way reflection between AOM and ARIA (with no validation)?

<div id="foo" role="banana">

would result in

console.log(foo.accessibleNode.role);  // banana

and

foo.accessibleNode.hidden = true;

would result in

<div id="foo" role="banana" aria-hidden="true">

Advantages

The main advantage of this approach would be that in there could be no conflict between AOM and ARIA [caveat below]: they would be the same thing. So a developer would never need to understand which one "wins".

It also improves the overall ergonomics of ARIA, and makes the connection between AOM and ARIA clearer.

Disadvantages

I seem to recall the two major reasons not to have two way reflection were:

  1. Relationship attributes
  2. Attributes "sprouting" on custom elements.

Relationship attributes

I would propose that the relationship attributes reflect the existing IDREF/IDREF list string, and that we create a separate API to allow setting an AccessibleNode reference.

<button id="disclosure" aria-controls="moredetails" aria-expanded="false">
<div id="moredetails">You can't parse [X]HTML with regex.</div>
<div id="moremoredetails">Regex is not a tool that can be used to correctly parse HTML.</div>
console.log(disclosure.accessibleNode.controls);  // "moredetails"
disclosure.accessibleNode.relatedNodes.controls = moremoredetails.accessibleNode;  // strawman syntax

We would need to figure out the precedence order between these two APIs. I would suggest ARIA take precedence, but I could be persuaded otherwise.

Custom Elements sprouting attributes

I would propose a Custom Elements-only API which would allow Custom Elements to have a "shadow" AccessibleNode which sets the "default" semantics for the element (which may vary over time for state properties), which would not sprout and may not be accessible to embedders.

This may be achieved either by hanging an AccessibleNode off the element's shadow root (requiring a shadow root to set "default" semantics), or else by a configuration option or lifecycle callback to give access to an internal AccessibleNode object.

LJWatson commented 7 years ago

Having played a little with the AOM, I'm inclined to agree with the status quo - that no reflection is ok.

I can understand the appeal of two way reflection now, but as the AOM and ARIA diverge, my hunch is that developers will favour the AOM over ARIA because it'll be more feature rich/capable. So keepin the separation clean from the outset would make things easier on developers in the long run I think.

Plus the need to add another API for Custom Elements eems to hadd another layer of complexity.

The current situation whereby ARIA trumps everything (in the acc name/desc computation for example), also suggests that developers might be ok with the notion in the context of the AOM too.

domenic commented 7 years ago

I think if two way reflection is implemented, then AOM becomes just a lot of properties that achieve nothing more than setAttribute/getAttribute already do, just with camelcase instead of snake-case.

In that case I would rather not add new APIs at all (except the element reference one you mention).

That is, with this proposal much of AOM stage 1 evaporates, as far as I can tell. This may be fine; maybe AOM stage 1 is too confusing and we should explore different APIs for accomplishing use cases like non-ID element references, or propagation of a11y information to custom elements. But it seems like a substantial change.

alice commented 7 years ago

@domenic I don't quite follow. What do we get without reflection that we don't get with it, if everything else stays the same?

domenic commented 7 years ago

It's more that adding reflection doesn't add anything to the platform that we don't have already, and we should never incur the signficant costs of a new feature for negligible benefit. See https://github.com/whatwg/html/blob/master/FAQ.md#wheres-the-harm-in-adding

alice commented 7 years ago

I'm just not clear on why this argument doesn't apply equally without reflection. Could you possibly clarify that?

domenic commented 7 years ago

Sure. Without reflection we're adding a fundamentally new capability: ability to set default properties on the accessibility tree in a way that native elements have, with ARIA overriding those defaults. This unlocks more composable and usable components in a similar way to custom elements.

With reflection we're just adding a new syntax to remember for setAttribute/getAttribute.

alice commented 7 years ago

Without reflection, though, we end up with two different APIs (i.e. el.setAttribute('role', 'button') vs el.accessibleNode.role = 'button') to do the same thing, with a non-intuitive distinction between them.

With reflection, combined with a mechanism for adding a hidden AccessibleNode for Custom Elements, we get both improved ergonomics for ARIA and a way to set implicit properties for Custom Elements via the same API, i.e. el.accessibleNode.role = 'button' vs (for example) this.shadowRoot.accessibleNode.role = 'button'.

domenic commented 7 years ago

They don't do the same thing; one is a new capability, and the other is the existing one with different syntax.

Reflection in my subjective opinion is worse ergonomics.

And reflection isn't the enabling feature to the custom element case. In fact you have to add a new workaround concept of hidden AccessibleNodes to recover from what you lose by adding reflection. You might as well just add the hidden AccessibleNode feature, with no reflection, and use setAttribute on it. (Or not have AccessibleNodes at all, and just have a way to propogate ARIA-set states/properties/roles into custom elements directly, or from shadow trees onto their container. That could be a huge improvement actually by avoiding the parallel-trees issue.) This is what I was alluding to by saying it'd be better to rescope v1 to focus on those new primitives, removing the now-redundant one.

robdodson commented 7 years ago

Reflection in my subjective opinion is worse ergonomics.

Can you explain a bit more why you think this? In my mind I was thinking about how <input> has the rather strange value behavior which doesn't reflect and so you end up with the (stale) attribute value and then the real value in the property. My concern is developers will lump ARIA attributes and accessibleNode properties together (since they achieve similar ends) and be confused why one overrides the other.

alice commented 7 years ago

They don't do the same thing; one is a new capability, and the other is the existing one with different syntax.

From the point of view of the developer, how would you explain the difference between the result of following two lines of code (using the current spec with no reflection)?

el.setAttribute('role', 'button');

el.accessibleNode.role = 'button';

with ARIA overriding those defaults (earlier)

Actually, as currently specced the AOM property overrides the ARIA role (and this is noted in Léonie's reponse as well).

Reflection in my subjective opinion is worse ergonomics.

Again, I'm not clear why this is the case.

In my experience, developers find the setAttribute() based API frustrating and confusing when virtually all other HTML attributes have associated properties. For example:

It feels very much second-class compared to attributes which have associated properties.

reflection isn't the enabling feature to the custom element case

Agreed. My argument is that it makes the relationship between ARIA and AOM easier to understand, and improves the ergonomics of ARIA (which we seem to currently disagree on).

In fact you have to add a new workaround concept of hidden AccessibleNodes to recover from what you lose by adding reflection.

Right, but this aligns more closely with the native element situation: a native element's accessibleNode doesn't expose its default semantics, for example button.accessibleNode.role for a native <button> element does not return "button", it returns null. With the hidden AccessibleNode version, we would have a similar distinction between a custom element's "built-in" semantics and its author-defined semantics.

... avoiding the parallel-trees issue ...

I'm also not clear on what the parallel-trees issue is. You can't tree-walk from a non-virtual AccessibleNode.

domenic commented 7 years ago

Can you explain a bit more why you think this?

Again, I'm not clear why this is the case.

In my subjective experience, setAttribute/getAttribute are better ergonomics for the following reasons:

For these reasons I personally think of reflecting to properties as an early platform mistake that we're propagating for consistency, not because it's a good idea.

If you think the ergonomic benefits are so great as to outweigh the cost, then I suggest splitting that into a separate proposal, since AOM is currently attracting a lot of interest from at least some quarters for the new capabilities it provides (before this proposal). If you're just interested in sugar for existing attribute manipulation, then that should be discussed separately, and preferably without ever involving AccessibleNode; just do it like every other HTML attribute. (Except for the boolean unfortunateness...)

From the point of view of the developer, how would you explain the difference between the result of following two lines of code (using the current spec with no reflection)?

The first line sets the ARIA DOM attribute. This is used as input by the browser in determining an element's role (e.g. it doesn't override strong native semantics).

The second line modifies the accessible node, which is an overriding overlay on top of normal element behavior. This layer is stronger than the normal DOM attribute, and is expected to be used by library or component authors which want to enforce strong semantics for their elements in a similar way to how the browser does.

Actually, as currently specced the AOM property overrides the ARIA role (and this is noted in Léonie's reponse as well).

Right, I'd forgotten this unfortunate decision :(. I think this is not as good as the opposite, but it's at least better than just pure reflection, because pure reflection gives negligible benefits over setAttribute/getAttribute, whereas this at least gives you the capability to set up something similar to strong native semantics.

My argument is that it makes the relationship between ARIA and AOM easier to understand

It does so by effectively gutting AOM, and just making it sugar for ARIA. If you're interested in making ARIA more ergonomic, I'd pursue that in a different spec, with no object model or claims of new capabilities involved. I'd hope then there'd be some group working on new capabilities of the type AOM originally promised, which I could be involved in.

Right, but this aligns more closely with the native element situation: a native element's accessibleNode doesn't expose its default semantics, for example button.accessibleNode.role for a native

Yes, this is another unfortunate decision. (I understand it's because of implementation complexity?) In general this discussion and the reminder of these past decisions is pushing me toward the belief that this whole properties-for-ARIA approach is not providing enough value and should be rethought.

Let's go back to the explainer and its use cases. It doesn't seem to separate them out very much from the proposed solutions, but reading through "Gaps in the web platform’s accessibility story", for stage 1 we have the following use cases being addressed:

  1. Allowing custom elements to avoid sprouting attributes
  2. Specifying relationships without using IDREFs (and thus being able to cross shadow DOM boundaries)

However, your OP explicitly lists these two things as disadvantages of your proposal, in that AOM no longer meets these two stage 1 use cases!

Instead, your proposal seems to address a new set of use cases, from https://github.com/WICG/aom/issues/88#issuecomment-330440509:

  1. Less typing
  2. Better autocomplete in IDEs
  3. Can use Object.assign() to set many properties at once.

But is there agreement in the group that these should be the new stage 1 use cases? I for one am much less interested in solving these than I am in solving the previous two, now-abandoned use cases. As such I'd love to separate off solving these use cases into a separate spec. Then, if you believe the currently-proposed solution for stage 1 use cases has too many disadvantages, we should scrap the solution---but not the use cases. (Instead of your proposal, which scraps the use cases, but keeps a modified version of the solution.)

I think it'd be very interesting to investigate new proposals for solving the custom elements problem (e.g. https://github.com/w3c/webcomponents/issues/567) and the IDREF problem (like your OP's "create a separate API to allow setting an AccessibleNode reference"---although it could just be a normal Element, not an AccessibleNode).

We can set aside the entire AccessibleNode idea as a false start, since we've found that (a) we don't actually want it to reflect the accessibility tree until stage 4, thus the original motivation of "exposing the accessibility tree as a primitive" is no longer present; (b) interaction between this AccessibleNode "overlay" tree and the normal DOM tree is too confusing (as claimed in this issue and the very long one that preceeded it). This would be a very nice simplification.

I'm also not clear on what the parallel-trees issue is.

It's this idea of there being a parallel overlay AccessibleNode tree, whose interaction with the DOM tree is confusing, and whose relationship to the actual accessibility tree is unclear. (You indeed have to tree walk via the owning element, but the parallel tree is still there.)

minorninth commented 7 years ago

Keep in mind that one of the reasons we have to add all of these properties is so that we can support virtual accessible nodes in Phase 3. (I've already started implementation of this, so we should be able to see some live demos of this soon.)

Virtual accessible nodes are critical for all of the use cases that involve canvas-based or other custom-drawn UIs, like those used in Google Maps, Google Spreadsheets, or even editors like Atom and CodeMirror where the input focus is not on the element that visually appears to be focused.

With virtual accessible nodes, it's critical that we have all of the ARIA attributes, plus more - and reflection is completely moot in that case.

I'm mainly pointing that out because one of the arguments I'm hearing here say that there'd be no point in AOM Phase 1 if it was just another way to access ARIA attributes. But that isn't true - it's still important as a foundation for Phase 3.

domenic commented 7 years ago

Then it should be delayed until Phase 3 is fully specced and agreed upon.