Support Keyed elements via an Attribute
At present, Keyed support exists for row and column children, and for keyed els. There are a few issues open to ask for support on other elements, and I think this is something that will continue. Rather than have people constantly ask for new elements to be added, it would be wonderful if the flexibility to opt into Keyed behaviour on any element could exist.
elm/html offers this via the Html.Keyed.node function (docs), but because of the higher level of abstraction offered by elm-ui this isn't a great fit. Elements like those in Input offer a bespoke API interface, so there'd need to be Keyed implementations of all these interfaces.
It's possible to wrap things in a keyed el now, but elm-ui's design can make this more challenging than it might be with an html interface. For example, what should the wrapper's width and height be? Instead of taking its dimensions from the caller, the wrapped content that needed keying will be taking them from the wrapper element. This ability to always do local layout reasoning is a strength of elm-ui, but it makes wrapper elements difficult to handle neatly.
Using an attribute would mean users can opt into Keyed support on an element-by-element basis without needing to introduce wrapper elements that affect the layout, as now.
Of course, prior art exists for this approach! React uses a key attribute for this purpose. A key attribute seems like a great fit for elm-ui! We gain the ability to key arbitrary content without needing intermediate wrapper elements that don't work well with elm-ui's model of "local layout reasoning". We also both improve the library's compositionality and improve the consistency of elm-ui, by avoiding the (String, Element msg) type that the Keyed module uses today.
The documented use-case for Keyed elements is to improve efficiency when DOM manipulation affects element collections, which is great. However, Keyed elements are also the solution to a whole class of issues where DOM state interacts poorly with the diffing / rearranging that the is performed by elm's virtual DOM under the hood.
The simple answer to this is "avoid DOM state", which is fine, But we know this isn't reality! It's super common in elm to use web components and custom elements to create little pockets of state, and there are other reasons to use DOM state as an escape hatch.
An example of this that is affecting me, is a CSS transition attribute on a button. I'm converting a Material application to elm-ui, and would like to preserve the smoothly changing background colour on buttons. It's possible to do this with animation in elm, but this is a really very massive jump in complexity for such a simple use-case, adding a CSS transition attribute to the button gets the job done simply.
Element.htmlAttribute <| HtmlAttr.style "transition" "background .2s"
Unfortunately, this introduces DOM state that the runtime is not aware of. If elm happens to reuse this button element between page states, a new page may have buttons appearing with unexpected colours and taking 0.2 seconds to settle into the expected appearance.
A solution to this is Element.Keyed, which ensures that these button elements don't get re-used. They'll always arrive fresh and in their correct state, ready to animate to another colour as needed.
This brings us back to the start of this Issue. Rather than ask for Element.Keyed support on buttons I think it's worth taking a step back to consider the Keyed module in elm/html. Is this the design we really want, or is this just how elm/html happens to work today? What if we used an Attribute for this instead of a dedicated Keyed API?
Thanks very much!