To introduce a new Button component for YUI 3 that addresses the following user stories
- "I want buttons on my site to look consistent & attractive."
- "I want to be able to programmatically control buttons in my application."
- "I want to my buttons to be intelligent and interact with one another in groups."
- "I want my application to be able to dynamically generate buttons."
- ARIA / Accessibility
- Modern styles (using CSS3), but degrades well to legacy browsers
- Customizable colors with minimal fuss
- Something lighter than Y.Widget, but similar in features to YUI2 buttons
- cssbuttons (CSS) - Some lite CSS skins to give the buttons a nice look & feel. This is ideal for someone who just wants a nice looking button, without needing programatic control.
- button-base (JS) - A
Y.Attribute
-driven wrapper around a button-like DOM node, and some helper utilities - button-group (JS) - A manager that listens for Y.Button events and can fire notifications when the selection has changed
Buttons can be used in many different ways. Some users just want the buttons to be aesthetically pleasing, others want to listen for clicks/events, others want to programmatically control them, and some will use them for core navigation groups. Because of these variety of use cases, it's important to have functionality logically and modularly separate, while keeping them simple to use & control.
The lightest possible implementation is just including the button stylesheet and adding the yui3-button
class to any element you would like to be a button. As requirements start to increase, you can start adding on JS modules to provide the required functionality. The JS portion of Button is very Y.Attribute
-driven. The idea is that it that Y.Button
is basically a wrapper around a DOM node that fills in missing functionality and keeps the UI in sync with the button state. Y.ButtonGroup
is also Y.Attribute
driven that knows about groups of Y.Button
instances and manages them as a whole.
Y.Button
- The Y.Attribute-driven Button objectY.Buttons
- A way to generate an array of Y.Button instances given a NodeListY.ButtonGenerator
- A way to dynamically generate a Y.Button instance with an unattached DOM node
Y.ButtonGroup
- A way to connect Y.Button instances together and has a memory of selection states
- onClick
- getDOMNode
- _colorToHex (static)
- _getContrastYIQ (static)
- type - specifies the type of button (push/toggle)
- disabled - a setter for the node's 'disabled' attribute
- selected - a setter that handles the node's 'selected' state
- backgroundColor - The background color for the button
- typeChange
- selectedChange
- backgroundColorChange
- disabledChange
- yui3-button
- yui3-button:hover
- yui3-button:active
- yui3-button-selected
- yui3-button-focused
- yui3-button-disabled
- type - The type of group (default:push/radio/checkbox)
- buttons - An array of
Y.Button
instances that are in this group - selection - The array of
Y.Button
instances that are currently selected
- role=button
- aria-pressed
- aria-selected
I haven't come up with a good reason for making ARIA support optional (like YUI 2 Button), so it's just baked in for the time being.
You can find some demos here.
-
Y.Button
- Add sugar & properties to not require users to use .get() & .set() all the time. This will improve usability & performance. -
Y.Button
- Support aria-label/aria-labeledby -
Y.Button
- Support icons & image buttons -
Y.Button
- Determine if the color contrast calculation should belong inY.Button
, or elsewhere -
Y.Buttons
- Combine with Y.Button? -
Y.ButtonGenerator
- Allow an optionalcontainer
element that the node is appended to? -
Y.ButtonGroup
- Support aria-multiselectable for radio groups -
Y.ButtonGroup
- Possibly support aria-owns ifY.Button
instance relationship is not parent-children -
Y.ButtonGroup
- 'selection' is probably inefficient. -
cssbuttons
- Add basic Sam & Night skins -
Allow using selector strings as opposed to requiring a Node/NodeList to instantiate.
-
Investigate state on legacy browsers
-
Investigate state on tablets
-
Investigate lazy attributes
-
Use the
event-touch
module to be more responsive on touchscreen devicesY.all('.yui3-button').on(['touchstart' /* <- if applicable */, 'click'], function (e) { e.halt(); // Stop event propagation // do something });
Absolutely make ARIA core. Not optional.
I don't understand what Y.Buttons is. And Y.ButtonGenerator seems like it could be a static method on Y.Button. I'd suggest trying to avoid creating too many new properties on Y. Y.Button and Y.ButtonGroup seems like enough.
Do you think it would make sense to have ButtonGroup be an ArrayList implementation?
A push button class should have a configurable click/pressed handler. Were you planning on this being handled by selected (seems awkward)?
So ButtonGroup would bring custom CSS to the page, or is that also in cssbuttons? I'm thinking about the typical button cluster looks.
FYI, the keyboard demo's delete key ignores tabs and deletes the character before the tab ;)
Push, toggle, and radio seem like they hit the 80% use case, but I foresee a request for a multi-state button. Keep that in mind when building the arch, so you don't make it harder to implement that down the line.
So are you imagining all DOM rendering will be via the ButtonGenerator? This is clearly a step to avoid the overhead of Widget (which I understand the reasoning for). There's overlap here with Satyen's WidgetStringRenderer gist (https://gist.github.com/1386836) and my DataTable design gist (https://gist.github.com/1356355) insofar as each of us is trying to make rendering lighter and/or more configurable. Specifically, Satyen is focusing on modifying Widget behavior, where I'm aiming at a similar approach to what you've laid out with a separate DOM/markup rendering mechanism. We need to come to consensus on preferred rendering patterns.
I think extending from Attribute instead of from Base is an interesting choice. Can you explain that? Base also brings plugin support and lifecycle, so you'd have to recreate the wheel if someone wanted to destroy() a Button instance. Extending View instead of Base also might offer some benefits. I wonder if it's possible to formulate the class logic into extensions, allowing implementers to Base.create a Widget button for the additional attributes/configurability of Widget. But still provide a lighter Y.Button class that included the class extension. That would require it to subclass Base, though.
Also, in the aim to reduce instantiation time, perhaps Y.extend is preferable to Base.create, since BuiltClass construction is certainly more complex than basic superclass chaining.
The focus on instantiation and rendering cost is supported largely by the use case of building a UI with a ton of buttons. In these scenarios, those buttons will almost certainly be in groups or clusters (UI region-wise, not co-joined buttons-wise), which suggests UI event handling is best done by delegation. This supports the notion of a ButtonGroup class capable of rendering the DOM of many buttons, but handling all the UX itself. That should be an instance-based solution to the desire to minimize instantiation and Widget's render() overhead. This could work in conjunction with a static DOM/markup factory method or methods. This is also reminiscent of the ModelList flyweight pattern for rendering discussion.
An invasive idea that will likely be shot down on philosophical grounds, but worth raising: augment the button Node with Attribute and add extra attributes directly. I imagine the Button API will be fairly small, so maybe mix it onto the Node instance directly. There are issues in the details here, of course.