Mac Preference Radios For The Web

One of my favorite UI features of the Mac OS, both OSX and the iPhone, is the way it handles radio inputs. Instead of having small, hard to click little round circle (like the web), there are large buttons that are obviously grouped by use of a connecting bar. The best place to see this is in the system preferences on OSX. Todays article, introduces a widget that will do the same, by converting existing radio buttons into a Mac Preference Radio.

Getting ready

You will need to download and include the macprefradio.js and macprefradio.css into your YUI 3 modules:
modules: {
	gallery-macprefradio: {
		requires: [widget,node,dom]

How to do it…

Consider the following markup for a traditional group of radio buttons:

<div id="myRadioId3">
	<input checked="checked" name="radio3" type="radio" value="1"/>
	<input name="radio3" type="radio" value="2"/>
	<input name="radio3" type="radio" value="3"/>
	<input name="radio3" type="radio" value="4"/>

Lets turn it into a Mac Preference Radio:

YUI().use(gallery-macprefradio, function(Y) {
	var radio3 = new Y.MacPrefRadio({boundingBox: #myRadioId3});

You may also convert radio buttons that do not have labels:

<div id="myRadioId2">
	<input checked="checked" name="radio2" type="radio" value="On"/>
	<input name="radio2" type="radio" value="Other"/>
	<input name="radio2" type="radio" value="Off"/>

Lets turn it into a Mac Preference Radio:

YUI().use(gallery-macprefradio, function(Y) {
	var radio2 = new Y.MacPrefRadio({boundingBox: #myRadioId2});

Additionally, you may skip the radio buttons altogether and implement the MacPrefRadio markup yourself:

<div class="yui3-macprefradio" id="myRadioId1">
	<div class="yui3-macprefradio-button yui3-macprefradio-left" title="yes">On</div>
	<div class="yui3-macprefradio-button yui3-macprefradio-right checked" title="no">Off</div>
	<input name="radio1" type="hidden" value="no"/>

Lets turn it into a Mac Preference Radio:

YUI().use(gallery-macprefradio, function(Y) {
	var radio1 = new Y.MacPrefRadio({boundingBox: #myRadioId1});

Lastly, you may render a radio button entirely from JavaScript by defining the radios property:

<div id="myRadioId4"></div>

YUI().use(gallery-macprefradio, function(Y) {
	var radios = [
		{value: a, text: A},
		{checked: true, value: b, text: B},
		{value: c, text: C},
		{value: d, text: D}
	var radio4 = new Y.MacPrefRadio({name: radio4, radios: radios});

There are two new events you can subscribe to:

function handleEvent(e) {
	var o = e.details[0];
	Y.log(oldvalue= + o.oldvalue);
	Y.log(newvalue= + o.value);
radio3.on(checked, handleEvent);
radio3.on(beforechecked, handleEvent);

The CSS you are including is:

.yui3-macprefradio {
	height: 1.75em;

.yui3-macprefradio .yui3-macprefradio-button {
	border: 1px solid #999;
	float: left;
	padding: 0.1em 0.5em;
	-webkit-border-radius: 6px; /* web-kit browsers */
	-khtml-border-radius: 6px; /* konquerer, works in safari too */
	-moz-border-radius: 6px; /* gecko browsers */
	-o-border-radius: 6px; /* possibly for opera */
	border-radius: 6px;
	text-align: center;
	width: 50px;

.yui3-macprefradio .checked {
	background-color: #DAF4DC;
	border-color: #7DC67D;

.yui3-macprefradio .yui3-macprefradio-center {
	border-left: none;
	-webkit-border-radius: 0;
	-khtml-border-radius: 0;
	-moz-border-radius: 0;
	-o-border-radius: 0;
	border-radius: 0;

.yui3-macprefradio .yui3-macprefradio-left {
	-webkit-border-bottom-right-radius: 0;
	-webkit-border-top-right-radius: 0;
	-khtml-border-bottom-right-radius: 0;
	-khtml-border-bottom-right-radius: 0;
	-moz-border-radius-topright: 0;
	-moz-border-radius-bottomright: 0;
	-o-border-bottom-right-radius: 0;
	-o-border-top-right-radius: 0;
	border-bottom-right-radius: 0;
	border-top-right-radius: 0;

.yui3-macprefradio .yui3-macprefradio-right {
	border-left: none;
	-webkit-border-bottom-left-radius: 0;
	-webkit-border-top-left-radius: 0;
	-khtml-border-bottom-left-radius: 0;
	-khtml-border-bottom-left-radius: 0;
	-moz-border-radius-topleft: 0;
	-moz-border-radius-bottomleft: 0;
	-o-border-bottom-left-radius: 0;
	-o-border-top-left-radius: 0;
	border-bottom-left-radius: 0;
	border-top-left-radius: 0;

How it works…

A set of radio buttons, is little more than a UI handling a group of like fields that can be set to several, mutually exclusive values. This widget replaces a group of radio inputs with the same number of Div elements that are grouped together, where one is styled to be checked. Behinds the scenes it maintains a hidden element with the same name attribute value as the radio inputs and a value set to the currently checked radio. This hidden element allows forms to submit and serialize correctly.

The markup created by the widget is a Div element to replace each radio input. These Div elements will have the class yui3-macprefradio-button applied and one of the following classes: yui3-macprefradio-left, yui3-macprefradio-center, yui3-macprefradio-right; depending on its position in the array of radio inputs. The title attribute of the Div element is the previous value of the radio input, and the content is the same as the content of the Label element element or it is also the value of the radio input. A class checked is applied when the checked.

The CSS makes use of rounded corners to style the elements. This is a CSS 3 property and will not be supported by all browsers, however, older browsers will fallback to square corners. Most of the styles will not need to be adjusted, however, the width, padding, and border-color of .yui3-macprefradio-button can be changed to meet your needs. Additionally, you will probably want to adjust the .yui3-macprefradio .checked styles.

The MacPrefRadio widget extends from the Widget modules, so you can leverage all its features, including listening for DOM events and attribute change listeners. When instantiating, simply pass in the parent element as the boundingBox and MacPrefRadio will handle the rest. Keep in mind that it will replace the content of everything inside of the boundingBox, so there should only be radio buttons and labels contained therein. If instantiating from markup containing radios without a Name attribute, or from JavaScript, you will need to define the name attribute. Normally, this is determined automatically.

You only need to supply the radios attribute, if rendering via JavaScript. Any values supplied to this attribute will supercede any elements in the DOM. A radio is represented by an object literal containing at least the value and text properties, and optionally the checked property. The value is the value that should be used for the radio, if the form is submitted. The text is the value that should be shown as a label for the radio. And checked is the checked state of the radio. If multiple checked values are provided, the last one in the array will be used.

The checked and beforechecked events are new events added by MacPrefRadio. They fire whenever you click on a new radio and it becomes selected. The event object passed into the callback will contain an array on the details property. The first value of that array is an object with two properties oldvalue, value. If the beforechecked event handler return false then the checking can be prevented.

Theres more…

There isnt really much more to say. This widget is backwards compatible with older browsers, will serialize, and doesnt affect markup if JavaScript inside supported. So you get the best of all worlds. However, if there is a missing feature, please let me know and Ill be sure to improve it. Lastly, I will be submitting this to the yui3-gallery soon, so look forward to seeing it there.

There is a Test Page available for you to play with and until the files are on the YUI 3 Gallery, you can download them here: