Enhancing TabView Accessibility with WAI-ARIA Roles and States

By YUI TeamJuly 30th, 2008

The YUI TabView Control is built on a strong
foundation of semantic markup that provides users with some basic accessibility. But while
TabView looks like a desktop tab control, screen readers don’t present it as an atomic
widget, leaving users to figure out how the various HTML elements that compose a TabView relate
to each other. However, through the application of the

WAI-ARIA Roles and States, it is possible to enhance
TabView’s accessibility such that users of screen readers perceive it as a desktop tab control.

A complete
example
of TabView using the WAI-ARIA Roles and States is available in the YUI Sandbox.
Watch a screen cast of the example
running in Firefox 3 with the NVDA screen reader
, or
download the latest development snapshot of
NVDA
and try it yourself.

Applying the WAI-ARIA Roles and States to TabView

Step 1: Getting Started

To start working with the WAI-ARIA Roles and States you’ll need both a browser and screen
reader that support ARIA. Both Firefox 3 and
Internet Explorer
8 Beta 1
have ARIA support. Trial versions of the leading
JAWS and

Window-Eyes screen readers are available for
free download. However, the open-source NVDA Screen
Reader
is the best option for developers as it is both free and provides excellent support
for ARIA.

Step 2: Adding Enhanced Keyboard Support

Out of the box, TabView provides basic keyboard support. Each Tab in a TabView is represented by
an <A> element whose href attribute is set to the id of an

<DIV> element that contains its content. In IE and Firefox,
<A> elements are automatically placed in the browser’s default tab index,
enabling the user to toggle between Tabs by pressing the tab key, and select a Tab by
pressing enter. (In Safari and Opera, <A> elements are not in the tab index
by default. Safari users can change this by going to the Safari Menu, Selecting “Preferences”,
then choose the “Advanced” tab and check the “Press Tab to highlight each item on a
webpage” checkbox.)

Having all of the Tabs in a TabView in the browser’s default tab index is a mixed blessing:
it provides basic keyboard accessibility, but can also make navigating more tedious in that users
navigating via the tab key have to tab through every Tab’s <A> as well as the
content of the active Tab’s correpsonding TabPanel in order to skip past the control. This problem
can be solved by setting the tabindex attribute of the <A> element
of the active Tab to a value of 0, and the inactive Tabs to -1. Setting an element’s

tabindex attribute to a value of -1 removes it from the browser’s default tab order,
while maintaining its focusability via JavaScript. Therefore, with this change in place it will be
easier for the user to skip over a TabView widget while navigating with the keyboard.

Mac vs. Windows

With only one Tab now in the browser’s default tab index, it will be necessary to supplement the
TabView with support for the arrow keys to enable the user to navigate between Tabs as they would
on the desktop. There are two different models for arrow key support for tabbed-content controls
in operating systems: Mac OS X and Windows.
On Windows, pressing the left or right arrow key moves focus to the next Tab and immediately
displays its corresponding TabPanel. On the Mac, with VoiceOver enabled, the arrow keys only
move focus between each Tab, and the user must press the space bar to load the content of the Tab’s
corresponding TabPanel. Of the two, the Mac’s model might be considered better for a DHTML TabView.
For example, if each Tab’s content is loaded via XHR, the Mac’s more intentional Tab selection
model could help prevent the user from making requests for data he/she is not interested
in consuming.

Supporting Multiple Orientations

The orientation
attribute of the TabView is used to render the Tabs on any of the widget’s four sides. To provide
arrow key support that will work regardless of the orientation of the Tabs, the left and up keys
will move the focus to the previous Tab, while the right and down arrow keys will move the focus to
the next Tab. As an additional convenience to the user, we’ll take another cue from the Mac’s
tab control implementation so that focus is automatically moved to the first or last Tab when the
user has reached the beginning or end of a list of Tabs.

To apply these keyboard enhancements to TabView, we’ll define a new prototype method named
enhanceAccessibility. This new method is designed to provide consistent keyboard
support for TabView across all of the A-Grade
browsers. It will work regardless of how the TabView is constructed (from existing markup, or
from script), its orientation, or if its content is static or loaded via XHR.

YAHOO.widget.TabView.prototype.enhanceAccessibility = function () {

	var Dom = YAHOO.util.Dom,
		Event = YAHOO.util.Event,
		UA = YAHOO.env.ua,

		oTabViewEl = this.get("element"),
		oTabList = Dom.getChildren(oTabViewEl)[0],
		aTabListItems = Dom.getChildren(oTabList),
		aTabs = this.get("tabs"),
		oTabIndexMap = {},
		oTab,
		oTabEl,
		oTabAnchor,
		oTabContentEl,
		oFocusedTabAnchor,
		sTabId,
		oActiveTab;


	//	Set the "tabIndex" attribute of each Tab's <A> element: The 
	//	"tabIndex" of the active Tab's <A> element is set to 0, the others to -1.
	//	This improves the keyboard accessibility of the TabView by placing
	//	only one Tab in the browser's tab index by default, allowing the user
	//	to easily skip over the control when navigating the page with the tab key.

	Dom.batch(oTabList.getElementsByTagName("A"), function (element) {
		element.tabIndex = -1;
	});
	

	oActiveTab = this.get("activeTab");

	if (oActiveTab) {
		Dom.getFirstChild(oActiveTab.get("element")).tabIndex = 0;
	}


	//	Returns the <A> element representing each Tab in the TabView.

	var getTabAnchor = function (element) {
	
		var oTabAnchor;
	
		if (Dom.getAncestorByClassName(element, "yui-nav")) {

			if (element.nodeName.toUpperCase() === "A") {
				oTabAnchor = element;
			}
			else {
				oTabAnchor = Dom.getAncestorByTagName(element, "A");
			}

		}
		
		return oTabAnchor;
	
	};


	//	Keydown event listener for the TabView that provides support for 
	//	using the arrow keys to move focus between each Tab.

	this.on("keydown", function (event) {
	
		var oCurrentTabAnchor = getTabAnchor(Event.getTarget(event)),
			oCurrentTabLI,
			oNextTabLI,
			oNextTabAnchor;


		if (oCurrentTabAnchor) {

			oCurrentTabLI = oCurrentTabAnchor.parentNode;

			switch (Event.getCharCode(event)) {

				case 37:	// Left
				case 38:	// Up

					oNextTabLI = Dom.getPreviousSibling(oCurrentTabLI);
					
					if (!oNextTabLI) { 
						oNextTabLI = aTabListItems[aTabListItems.length-1];
					}
				
				break;

				case 39:	// Right
				case 40:	// Down

					oNextTabLI = Dom.getNextSibling(oCurrentTabLI);
					
					if (!oNextTabLI) { 
						oNextTabLI = aTabListItems[0];
					}
				
				break;
			
			}

			oNextTabAnchor = Dom.getChildren(oNextTabLI)[0];

			if (!oFocusedTabAnchor) {
				oFocusedTabAnchor = oCurrentTabAnchor;			
			}

			oFocusedTabAnchor.tabIndex = -1;
			oNextTabAnchor.tabIndex = 0;

			oNextTabAnchor.focus();

			oFocusedTabAnchor = oNextTabAnchor;

		}

	});

};

Step 3: Adding the WAI-ARIA Roles and States

With the keyboard functionality in place, we’ll proceed with the application of the WAI-ARIA Roles
and States. Once applied, assistive technologies (AT) such as a screen reader will no longer
announce the HTML elements that compose the TabView as HTML elements, but as a tab control. In
this way the relationship between the WAI-ARIA Roles and States and HTML is similar to that of CSS:
both enable the developer to change the presentation of markup. And since the WAI-ARIA Roles and
States enable the TabView to be presented to the user as a desktop tab control, it makes the
previous work of applying desktop-like keyboard behavior all the more critical. If users of AT are
going to preceive the TabView as a desktop tab control, it needs to fulfill that expectation from a
keyboard perspective.

As a best practice, apply the WAI-ARIA Roles and States via JavaScript. Since the WAI-ARIA Roles
and States depend on JavaScript-based keyboard functionality, it follows that the attributes
representing the WAI-ARIA Roles and States only be applied via JavaScript. This
Progressive Enhancement
strategy ensures the best possible user experience by only applying WAI-ARIA Roles and States when
the browser technologies required to support them (in this case, CSS and JavaScript) are available.

Roles and states are added to a TabView’s DOM elements via the
setAttribute
method. At present only two browsers have WAI-ARIA support:
Firefox 3 and
Internet Explorer 8 Beta 1.
(The changelog for Opera 9.5
mentions support for screen readers, MSAA, and ARIA, but in my testing in Opera I didn’t
find ARIA to work.) Therefore, we’ll make use of YUI’s browser detection
(YAHOO.env.ua) and
only apply the Roles and States to browsers that support them. The role of
tab will be applied to each Tab’s

<A> element, and the role of
tablist to their parent
<UL>. Finally, each Tab’s content element (<DIV>) will
receive the role of tabpanel and
an aria-labelledby attribute
with a value of the id of the <A> representing its corresponding Tab instance.
The aria-labelledby attribute enables the screen reader to announce the label of the
Tab for each TabPanel when the first element in a TabPanel receives focus, providing the user with
some context as to where they are. The following example illustrates how the ARIA roles and
properties are applied to each of the HTML elements that compose a TabView:

<div class="yui-navset">
	<ul role="tablist">
		<li>
			<a href="..." id="tab-1" role="tab">tab label</a>

		</li>
	</ul>
	<div clas="yui-content">
		<div role="tabpanel" aria-labelledby="tab-1">tab content</div>

	</div>
</div>
Screen-Reader Specific Tweaks

The implementation of the WAI-ARIA Roles and States is slightly different across screen readers, so
it is necessary to make some additional tweaks. A role of
presentation will need to be
applied to the parent <LI> element of each <A>, so that
the Window-Eyes screen reader recognizes that each Tab belongs to the same TabList. For JAWS it is
necessary to remove the href attribute of each Tab’s <A> element
to prevent it from announcing the attribute’s value when focused. Ideally JAWS would behave like
NVDA and Window-Eyes and allow the applied role attribute of

tab to take precedence over the
default role of the <A> element. The following illustrates the updated
markup for a TabView with the screen reader tweaks applied:

<div class="yui-navset">
	<ul role="tablist">
		<li role="presentation">

			<a id="tab-1" role="tab">tab label</a>
		</li>
	</ul>
	<div clas="yui-content">
		<div role="tabpanel" aria-labelledby="tab-1">tab content</div>

	</div>
</div>

With this strategy for applying the WAI-ARIA Roles and States to TabView, we can update the
enhanceAccessibility method:

YAHOO.widget.TabView.prototype.enhanceAccessibility = function () {

	var Dom = YAHOO.util.Dom,
		Event = YAHOO.util.Event,
		UA = YAHOO.env.ua,

		oTabViewEl = this.get("element"),
		oTabList = Dom.getChildren(oTabViewEl)[0],
		aTabListItems = Dom.getChildren(oTabList),
		aTabs = this.get("tabs"),
		oTabIndexMap = {},
		oTab,
		oTabEl,
		oTabAnchor,
		oTabContentEl,
		oFocusedTabAnchor,
		sTabId,
		oActiveTab;


	//	Set the "tabIndex" attribute of each Tab's <A> element: The 
	//	"tabIndex" of the active Tab's <A> element is set to 0, the others to -1.
	//	This improves the keyboard accessibility of the TabView by placing
	//	only one Tab in the browser's tab index by default, allowing the user
	//	to easily skip over the control when navigating the page with the tab key.

	Dom.batch(oTabList.getElementsByTagName("A"), function (element) {
		element.tabIndex = -1;
	});
	

	oActiveTab = this.get("activeTab");

	if (oActiveTab) {
		Dom.getFirstChild(oActiveTab.get("element")).tabIndex = 0;
	}


	//	Returns the <A> element representing each Tab in the TabView.

	var getTabAnchor = function (element) {
	
		var oTabAnchor;
	
		if (Dom.getAncestorByClassName(element, "yui-nav")) {

			if (element.nodeName.toUpperCase() === "A") {
				oTabAnchor = element;
			}
			else {
				oTabAnchor = Dom.getAncestorByTagName(element, "A");
			}

		}
		
		return oTabAnchor;
	
	};


	//	Keydown event listener for the TabView that provides support for 
	//	using the arrow keys to move focus between each Tab.

	this.on("keydown", function (event) {
	
		var oCurrentTabAnchor = getTabAnchor(Event.getTarget(event)),
			oCurrentTabLI,
			oNextTabLI,
			oNextTabAnchor;


		if (oCurrentTabAnchor) {

			oCurrentTabLI = oCurrentTabAnchor.parentNode;

			switch (Event.getCharCode(event)) {

				case 37:	// Left
				case 38:	// Up

					oNextTabLI = Dom.getPreviousSibling(oCurrentTabLI);
					
					if (!oNextTabLI) { 
						oNextTabLI = aTabListItems[aTabListItems.length-1];
					}
				
				break;

				case 39:	// Right
				case 40:	// Down

					oNextTabLI = Dom.getNextSibling(oCurrentTabLI);
					
					if (!oNextTabLI) { 
						oNextTabLI = aTabListItems[0];
					}
				
				break;
			
			}

			oNextTabAnchor = Dom.getChildren(oNextTabLI)[0];

			if (!oFocusedTabAnchor) {
				oFocusedTabAnchor = oCurrentTabAnchor;			
			}

			oFocusedTabAnchor.tabIndex = -1;
			oNextTabAnchor.tabIndex = 0;

			oNextTabAnchor.focus();

			oFocusedTabAnchor = oNextTabAnchor;

		}

	});


	//	Only apply the WAI-ARIA Roles and States for FF 3 and IE 8 since those
	//	are the only browsers that currently support ARIA.
	
	if ((UA.gecko && UA.gecko >= 1.9) || (UA.ie && UA.ie >= 8)) {

		//	Set the "role" attribute of the <UL> encapsulating the Tabs to "tablist"

		oTabList.setAttribute("role", "tablist");
		
	
		for (var i = 0, nLength = aTabs.length; i < nLength; i++) {
		
			oTab = aTabs[i];
			oTabEl = oTab.get("element");
			oTabAnchor = Dom.getChildren(oTabEl)[0];


			//	Create a map that links the ids of each Tab's <A> element to  
			//	the Tab's "index" attribute to make it possible to retrieve a Tab
			//	instance reference by id.

			sTabId = oTabAnchor.id;
		
			if (!sTabId) {
				sTabId = Dom.generateId();
				oTabAnchor.id = sTabId;
			}
	
			oTabIndexMap[sTabId] = i;


			//	Need to set the "role" attribute of each Tab's <LI> element to 
			//  "presentation" so that Window-Eyes recognizes that each Tab belongs to 
			//	the same TabList. Without this, Window-Eyes will announce each Tab as  
			//	being "1 of 1" as opposed to "1 of 3," or "2 of 3".

			oTabEl.setAttribute("role", "presentation");

			oTabAnchor.setAttribute("role", "tab");



			//	JAWS announces the value of the "href" attribute of each Tab's <A>  
			//	element when it recieves focus.  Ideally JAWS would allow the 
			//	applied "role" attribute of "tab" to take precedence over the default   
			//  role of the <A> element like NVDA and Window-Eyes do.  It is 
			//	possible to fix this problem by removing the "href" attribute from 
			//	the <A>.

			oTabAnchor.removeAttribute("href");
	

			oTabContentEl = oTab.get("contentEl");

			oTabContentEl.setAttribute("role", "tabpanel");
			

			//	Set the "aria-labelledby" attribute for the TabPanel <LI> element to 
			//	the id of its corresponding Tab's <A> element.  Doing so enables the 
			//	screen reader to announce the label of the Tab for each TabPanel when  
			//	the first element in a TabPanel receives focus, providing the user  
			//	with some context as to where they are.
			
			oTabContentEl.setAttribute("aria-labelledby", sTabId);
		
		}


		//	Add a keypress listener that toggles the active Tab instance when the user 
		//	presses the Enter key.  This is necessary because the removal of the "href" 
		//	attribute from each Tab's <A> element (for JAWS support) causes the 
		//	TabView's default Enter key support to stop working.  Support for the Space
		//	Bar is also added as an additional convience for the user.

		this.on("keypress", function (event) {
		
			var oTabAnchor = getTabAnchor(Event.getTarget(event)),
				nCharCode = Event.getCharCode(event);
	
			if (oTabAnchor && 
				(nCharCode === 13 || nCharCode === 32) && 
				(oTabAnchor.parentNode !== this.get("activeTab").get("element"))) {

					this.set("activeIndex", oTabIndexMap[oTabAnchor.id]);
			
			}
		
		});
	
	}

};

Step 4: Putting It All Together

To test the new enhanceAccessibility method, we'll use the
Getting Content from an
External Source example
from the existing TabView examples gallery as a starting point.
Once the TabView instance has been appended to the page, we'll call the new
enhanceAccessibility method. Next we'll use some additional WAI-ARIA Roles and States
to make some example-specific tweaks. First we'll, use the
describedby

property to provide some helpful instructional text that will be announced to the user
when the TabView initially receives focus. Since each Tab's content is loaded asynchronously, we'll
also leverage
WAI-ARIA Live Regions to
message users when a Tab's content is both being loaded and has finished loading.
(Note: The describedby property and Live Regions are currently only supported in the
latest development snapshots of NVDA.)
The following code snippet illustrates how it all comes together:

(function() {

	var oTabView = new YAHOO.widget.TabView();

	oTabView.addTab( new YAHOO.widget.Tab({
		label: "Opera",
		content: "<p>Please wait.  Content loading.</p>",
		dataSrc: "news.php?query=opera+browser",
		cacheData: true,
		active: true
	}));

	oTabView.addTab( new YAHOO.widget.Tab({
		label: "Firefox",
		content: "<p>Please wait.  Content loading.</p>",
		dataSrc: "news.php?query=firefox+browser",
		cacheData: true
	}));

	oTabView.addTab( new YAHOO.widget.Tab({
		label: "Explorer",
		content: "<p>Please wait.  Content loading.</p>",
		dataSrc: "news.php?query=microsoft+explorer+browser",
		cacheData: true
	}));

	oTabView.addTab( new YAHOO.widget.Tab({
		label: "Safari",
		content: "<p>Please wait.  Content loading.</p>",
		dataSrc: "news.php?query=apple+safari+browser",
		cacheData: true
	}));


	oTabView.appendTo("container");
	oTabView.enhanceAccessibility();


	var Dom = YAHOO.util.Dom,
		UA = YAHOO.env.ua,
		oActiveTab,
		oTitle,
		oTabViewEl,
		oLog,
		sInstructionalText;


	//	Only apply the WAI-ARIA Roles and States for FF 3 and IE 8 since those
	//	are the only browsers that currently support ARIA.
	
	if ((UA.gecko && UA.gecko >= 1.9) || (UA.ie && UA.ie >= 8)) {

		oActiveTab = oTabView.get("activeTab");


		//	Append some instructional text to the <H2>

		oTitle = Dom.get("tabview-title");

		sInstructionalText = oTitle.innerHTML;

		oTitle.innerHTML = (sInstructionalText + "<em id=\"tabview-description\">Press the space bar or enter key to load the content of each tab.</em>");


		//	Set the "aria-describedby" attribute of the <UL> with the role of "tablist"
		//	to the id of the <EM> inside the <H2>.  This will trigger the screen reader 
		//	to read the text of the <EM> when the TabView is initially focused, 
		//	providing some additional instructional text to the user.  (Currently this 
		//	only works with the NVDA screen reader.)

		Dom.getChildren(oTabView.get("element"))[0].setAttribute("aria-describedby", "tabview-description");
		

		//	Append a live region to the TabView's root element that will be used to 
		//	message users about the status of the TabView.

		oTabViewEl = oTabView.get("element");
		oLog = oTabViewEl.ownerDocument.createElement("div");

		oLog.setAttribute("role", "log");
		oLog.setAttribute("aria-live", "polite");

		oTabViewEl.appendChild(oLog);


		//	"activeTabChange" event handler used to notify the screen reader that 
		//	the content of the Tab is loading.

		oTabView.on("activeTabChange", function (event) {

			var oTabEl = this.get("activeTab").get("element"),
				sTabLabel = oTabEl.textContent || oTabEl.innerText,
				oCurrentMessage = Dom.getFirstChild(oLog),
				oMessage = oLog.ownerDocument.createElement("p");

			oMessage.innerHTML = "Please wait.  Content loading for " + sTabLabel + " property page.";

			if (oCurrentMessage) {
				oLog.replaceChild(oMessage, oCurrentMessage);
			}
			else {
				oLog.appendChild(oMessage);						
			}

		});	
	

		//	"dataLoadedChange" event handler used to notify the screen reader that 
		//	the content of the Tab has finished loading.
		
		var onDataLoadedChange = function (event) {

			var oTabEl = this.get("element"),
				sTabLabel = oTabEl.textContent || oTabEl.innerText,
				oCurrentMessage = Dom.getFirstChild(oLog),
				oMessage = oLog.ownerDocument.createElement("p");

			oMessage.innerHTML = "Content loaded for " + sTabLabel + " property page.";

			if (oCurrentMessage) {
				oLog.replaceChild(oMessage, oCurrentMessage);
			}
			else {
				oLog.appendChild(oMessage);						
			}
		
		};
	
		oTabView.getTab(0).on("dataLoadedChange", onDataLoadedChange);
		oTabView.getTab(1).on("dataLoadedChange", onDataLoadedChange);
		oTabView.getTab(2).on("dataLoadedChange", onDataLoadedChange);
		oTabView.getTab(3).on("dataLoadedChange", onDataLoadedChange);

	}

})();

Further Reading and Resources

13 Comments

  1. Wow excellent article Todd! I’ve been working on adding ARIA to my Accordion widget and I know I’m not quite there yet. This article will definitely help me get it right.

    Thanks!

  2. I agree with Marco, great article! Will this level of ARIA support will be baked into YUI widgets?

    I’m very impressed with your level of detail, especially regarding the tweaks required to get a nice experience with existing screen reader implementations.

    Thanks for sharing this. I’ve linked to it from Code Talks where we are trying to provide developers with an entry point into tutorials like this.

  3. After thinking about all this a bit more, two questions popped up in my head:

    1: Why restrict the use of ARIA attributes to only FF3 and IE8? I probably wouldn’t because browsers that don’t support it will just silently ignore the attributes and more importantly, because it would mean you have to constantly update the code as more browsers with ARIA support get released.

    2: Why remove the HREF attribute on links? As far as I know JAWS ONLY announces the HREF when there is no link text. If there is, it announces the link text and not the HREF. Therefore I don’t know why we should remove this attribute and therefore introduce other issues to non-screenreader browsers that then need fixing.

  4. Todd Kloots said:
    August 1, 2008 at 9:42 am

    Hey Marco –

    Regarding #1: I was motivated primarily by performance. Since there is always a cost associated with modifying the DOM, I figured it didn’t make sense to apply the WAI-ARIA Roles and States in older browsers that don’t support ARIA. However, for those that do, I did design the browser sniffing to account for future versions of FF and IE. Additionally, in my experience using ARIA, I haven’t found it has reached a level of maturity where I can apply the roles and states and it will just work out of the box. For that reason, I prefer to only provide functionality when I have tested it and am confident that it will work for the user.

    Regarding #2: I tested using JAWS 9, and found that with the Virtual Buffer off (required for ARIA and the keyboard functionality to work), JAWS would in fact read the value of the “href” attribute. For example, when the Firefox tab receives focus, JAWS reads: “Firefox tab http:// ….”. For that reason, I removed the “href” attribute. I don’t see any problem removing the attribute, as the anchor itself is only there for the C-Grade browser experience when the user is building a TabView from existing markup. In an A-Grade browser, the anchor element doesn’t play a functional role after the TabView has been instantiated.

    – Todd

  5. Todd, your knowledge and time are much appreciated.

    Thank you.

    Cheers

  6. [...] during the show, Victor Tsaran from Yahoo! posted an excellent tutorial on how the YUI tab control is enhanced with WAI-ARIA. With this level of detail, YUI is bound to achieve excellent [...]

  7. [...] Tsaran from Yahoo! has posted an article on the YUI blog titled Enhancing TabView Accessibility with WAI-ARIA Roles and States. It is a hands-on example that shows a lot of techniques outlined by Gez in action. There are both [...]

  8. [...] Enhancing TabView Accessibility with WAI-ARIA Roles and States – Todd Kloots [...]

  9. So far so good, unfortunately all the time put into getting better TabView Accessibility with WAI-ARIA is blown-up by the fact that only two browsers support it for now – Firefox 3 and Internet Explorer 8 Beta 1

  10. [...] Yahoo developers are cooking up more YUI-ARIA widgets, which you can read about on the YUI blog: Enhancing TabView Accessibility with WAI-ARIA Roles and States. And don’t forget Gez Lemons’ recently published Introduction to WAI [...]

  11. [...] posted an explanation on the YUI blog about how to use WAI-ARIA Roles and States to enhance the accessibility of the YUI TabView Control. The component provides a tabbed interface using semantic markup, indicating changes of state not [...]

  12. Hi Todd,

    I’ve been working on applying these techniques to my AccordionView widget.

    I’m baffled by the fact that when I remove the href attribute, Safari won’t let me focus the element anymore. Yet, in your example it seems to work but different looking (not the safari ‘glow’ effect on focus but a dotted line).

    When I put the href attribute back, all is fine in Safari.

    Is there something I’m overlooking? I can’t for the world figure it out…

  13. [...] guys? We can only hope. Oh, and a great article about accessible rich internet applications at Yahoo! to top it off [...]