Category Archives: accessibility

CSS Strategies for resizable text

The WCAG guidelines on resizable text state that you should be able to scale text by 200%.

How to test:
In Firefox, got to View -> Zoom -> Zoom Text Only. Then when you are back in your page, zoom up to 200% by pressing Ctrl + 6 times.

Possible problems that happen when only text gets bigger:

  • Text that wraps underneath itself has too little space between the letters. You’ve set your line-heights in px.
  • using height which causes text to get cut when it overflows that height
  • Items lie on top of one another, because you’re using absolute positioning
  • text stops sitting on a particular background colour and becomes hard to see (or just looks untidy)

SCENARIO: I have an element that must be at least x pixels tall, but could grow larger if I put more text in there
SOLUTION: use min-height rather than height, remember that older IE will treat height as min-height so use your .ie6 styles or whatever you are using to set it seperately

SCENARIO: I have an element that must be no more or less than a specific height
SOLUTION: set the height in ems rather than px and the box will grow with the text resizing

SCENARIO: text is sitting on top of other text or items and is illegible
SOLUTION: rework the layout using floats or relative positioning, negative margins, or if you have fixed dimensions for your absolutely positioned item (e.g. a close icon on a lightbox), style your surrounding boxes so that their margins sit on the area where the absolutely positioned item is. At worst you may have to use a bit of JS to refine the position (e.g. you need to align something to the bottom of another element) but that is mixing styling rules with behaviour rules.

SCENARIO: my text no longer sits on a coloured background, I’ve run out of background image
SOLUTION: rebuild so that the background surrounds the content section of your box, which has a background color. The background adjusts as the size of the content grows and shrinks because it sits around it rather than under it
LEAST DESIRABLE SOLUTION: wrap your text in an element that gets given a background color

SCENARIO: there’s no longer enough space between the lines of text
SOLUTION: don’t set line-height in pixels, set it in ems, and make less use of them because as you use relative units, you’ll find that they are a pain to maintain when they start getting inherited.

Make JavaScript updates (including AJAX) accessible

Towards the end of Laura Kalbag‘s excellent talk on accessibility at last night’s FrontEndLondon meetup, a question was asked about how to deal with when JavaScript updated the page.  There wasn’t really time to go through it, and really its something best shown with a demo.

There are a couple of aria attributes that are well-supported by screenreaders that can help you out with this.

Here’s a pared-down example of an implementation.

<ul aria-live="assertive" aria-relevant="all" id="list">
    <li>item one</li>
    <li>item two</li>
<button type="button" id="addnew" aria-controls="list">Add new item</button>
<button type="button" id="remove" aria-controls="list">Remove last item</button>
//assuming jquery

window.onload = function () {
//NB: this is not how you'd build a module in production, its pared down for clarity of what's important.
var mymodule = (function ($) {
  return function () {
    var addBtn = $("#addnew");
    var removeBtn = $("#remove")
    var list = $("#list");

    var addItem = function () {
      var xhr = $.ajax({url:"/"});
      list.attr("aria-busy", "true");//apply the busy state
      xhr.done(function (data) {
        var newLi = $('<li>New item</li>');
      }); () {
        alert("something went wrong");
      xhr.always(function () {
        list.attr("aria-busy", "false"); // don't just do this on success, do it always. your server may return an error.
    var removeItem = function (e) {
      var c = list.children();
      c.eq(c.length - 1).remove();

    addBtn.on("click", addItem);
    removeBtn.on("click", removeItem);

If you run this example, you will notice that the announcement just consists of the new text. For example, you’d hear “Remove last item.” then upon activating it you’d hear “item two”. There’s no further information such as whether the content has been added or removed. If your feature doesn’t make sense without that extra bit of information, then you’d want to do something slightly different, where you update the text of a separate element with aria-live set on it with the fuller message.

Note that ‘add’ makes an ajax call, but ‘remove’ just acts on the DOM. Both get announced.

There are 2 attributes you will definitely need:

  • aria-live
  • aria-busy

There are 3 attributes you will possibly need:

  • aria-atomic
  • aria-relevant
  • aria-controls

These aria attributes sit on block-level elements that contain the content that is going to change.


Indicates that this is a part of the page that will get updated even after the page has loaded.

When the element with this attribute has a content change, the assistive technology automatically picks up on it and deals with it without you having to do anything more.

Its values are: off (ignore changes, default), polite (announce new content when you’ve run out of content to read), assertive (announce new content at the first opportunity).

There used to be aria-live=”rude” but it was so badly abused and disrupted users so much that it got dropped.  Be considerate when using assertive.  Confirming a save action probably justifies assertive.  An updated news feed is better being polite.


Indicates that an element’s content is in the process of changing.  Its values are true or false.

Unlike aria-live, this one is something that you’d dynamically update from JavaScript.

A good rule of thumb is that if you are showing a spinner over something to indicate activity, you probably also want to set aria-busy to true on the container element for the area is getting updated, and to false when it hides.


Should the whole area be announced when just one part of if has changed? Or just the bit that changed?

Its values are true (announce the whole region when something changes) or false (default).

TIP: if your area’s text is not being read out when it updates, sometimes adding aria-atomic=”true” fixes it.


What type of changes should be announced?

Values are one or more of additions (meaning elements being added), text, removals (again, of elements) and all, separated by a space.  Default is aria-relevant=”additions text”


Indicates a relationship between an interactive element (like a link or button) and the element it changes.

Value is ID of the controlled element.


Mac: System Preferences > Universal Access

From there you can check the box to Show Universal Access status in the menu bar for easier access.

You will get best results from working with Safari or Chrome rather than Firefox on Mac.

Windows: Visit Freedom Scientific and download the demo version of JAWS.  After 20 minutes of use, it stops and you have to reboot to get it going again, so plan your test time accordingly.

NVDA is another free, popular screenreader but has fewer functions than the longer-established, paid-for JAWS.

Discover the reading level of your website. (Maybe).

In April, Google added a feature where you can find (and filter by) reading difficulty level (

While their instructions work on the assumption that you’re looking for a particular search term by reading level, it is also possible to combine it with the ‘site’ operator.  Does this get a thumb-in-the-air approximation for the reading difficulty level of a website? Probably, but Google’s not saying. Its certainly worth giving it a go.

Why might this be useful? You probably have in mind who you would like your audience to be – who you write for.  The ‘pass/fail’ result you get will depend on the abilities of your intended audience.  If the least able members of that audience struggle to comprehend what you’ve written, then you’ve failed.  On the other hand, if your target audience is highly educated and specialised then the advanced reading level is a way to filter out the entry-level content. Ability can be affected by a range of things – your audience may be still at school or may have a low reading age, may have problems concentrating (that could be a cognitive disability, or it could be environmental like a noisy office or screaming kids in the garden), may be new to your subject or may have a different first language with only an intermediate level of fluency in your chosen language.


Instead of a regular search term, type Once on your search results page, look for the ‘Search tools’ button at the top, click it to show the extra tools and then choose ‘Reading level’ from the list opened by clicking ‘All results’

Example results

For my own site, I’d consider the most basic level of my intended audience to be new-ish to the topics I cover, so I’m fine with this split.

Reading difficulty results for this website shows 14% basic, 86% intermediate and 0% advanced reading difficulty level

News / journalism

BBC News.  Here’s the conundrum for them – while ‘dumbing down’ is broadly undesirable, license fee payers cover the whole spectrum of intelligence.  How do you avoid someone being under-served by a service for which they contribute financially on the grounds that they’re not the sharpest knife in the drawer?

BBC news reading difficulty is on average higher than the BBC as a whole

Compare with the BBC as a whole:

Reading difficulty results for show 38% basic, 60% intermediate and 1% advanced

No surprises for the Sun

Sun newspaper has very little advanced reading difficulty content

If you are not British you won’t understand why the image below is amusing. Sorry about that.

The Daily mail is almost entirely basic level reading

The Guardian is fairly consistent in its style.

The Guardian is predominantly intermediate reading difficulty, but has little advanced level content

Learning and education

The world wide web consortium.  Subjects that fall under advanced are SMIL, Voice Browser Working Group, Internationalization, QA and a few other bits and pieces. Would they benefit from a rewrite to something with a lower cognitive barrier to entry? content has a comparitively large amount of advanced level reading

html5 doctor

Key Stage 1 – kids aged between 5 and 7

KS1 has easy reading levels

GCSE revision help – exams sat typically at 16 years old in England and Wales

GCSE has a mixed range of reading levels content is mostly academic and highly specific post-graduate level material. Today’s homepage includes links to articles such as “Helicity dependent directional surface plasmon polariton excitation using a metasurface with interfacial phase discontinuity” and “Proof mooted for quantum uncertainty”.

Difficulty reading levels for, with less than 1% basic, 3% intermediate and 97% advanced


Hansard is the publisher of the UK Parliamentary proceedings – i.e. all the debates and written questions get transcribed and put onto the internet

Paliamentary debates in the uk commons are almost entirely intermediate level

Kildare street is the Irish equivalent

kildare street has a similar profile to hansard is where all the UK legislation is published.  Unsurprisingly, very little of it is easy reading though there is less ‘advanced’ level content than I’d expected.

uk legislation has 18% advanced level is the site where the government puts all its guidance and services, ranging from how to renew your car tax to detailed guidance on depleted uranium policy. has a wide range of reading levels


The new hotness content is mostly basic and intermediate, with a few advanced articles

The old hotness

blogger reading level is similar to medium

WordPress seems to have attracted more academic bloggers. E.g. the first result under advanced is “Indigenous Peoples and Reducing Emissions from Deforestation & Degradation”

wordpress blogs have more advanced level content at 14%

Nature blogs have a significantly different profile to

Screen Shot 2013-06-26 at 15.58.04

You’re not going to get much complexity in 140 characters

Twitter mostly basic reading level

However, I’m wondering why this is falls under ‘advanced reading’. Is the correct usage of apostrophe but cavalier disregard for question marks the cause?

Supposedly advanaced level reading tweet reads 'Why does Kia feel the need to snapchat me on the toilet can't she leave me alone for 5 seconds'


Accessible select box (dropdown) change events that work cross-browser, including IE, Chrome and touch devices

There’s a well-know issue with select elements where if you listen for the change event, it will trigger differently between browsers, and on IE it will trigger in a way that makes it inaccessible.

Take the following example: user tabs onto a select element and then uses the arrow key to move through the list. In IE (tested versions 7-9), this triggers the change event whereas other browsers will wait until you’ve moved off the element.

Try this demo on jsfiddle, it will alert the value of the option you’ve selected when ‘change’ event gets fired.

If you are using your select element for navigating to another page, the keyboard user will never be able to get past the first and second item in your list. If you are using the select element for loading in more content via AJAX (e.g. updating other field items based on a select box selection is quite common), then you are going to be hitting your server far more than necessary.

The solution is to bind to blur, click and keydown for IE and change for everything else (should include touch devices), and then a) filter out keypresses that aren’t spacebar or enter and b) check to see if the selected option has actually changed. The below code uses jQuery, but it is all possible via native JS or other libraries. Sadly it relies on checking the browser’s user agent as there isn’t a feature you can test for (if I’ve overlooked something I’d love to hear about it).

Have a look at the accessibile version on jsfiddle

var somemodule = (function () {
  var obj = {
    //some code
    bindDOM: function () {
      var evts = "";
      //accessibility workaround: onchange is good for non-ie; keydown, click and blur is good for ie (but not for chrome)
      //unfortunately we cannot detect this with feature support so must rely on browser support
      if ($jQ.browser.msie) {
        evts = "keydown click blur";
      else {
        evts = "change";
      this.elem.on(evts, $jQ.proxy(this.onFooChange, this));
    onFooChange: function (e) {
      var id;
      if (e.type !== "keydown" || (e.which === 13 || e.which === 32)) {
        id = $jQ(;
        if (id !== this.currentFoo) {
          this.currentFoo = id;
  //more code
  return obj;

Interface Developer’s quality checklist

Towards the end of a project or sprint, time to delivery can sometimes get crushed due to any number of reasons (late wireframe or design delivery, too many iterations, staff illness, contractors leaving, underestimation, overdelivering, changing business needs, etc.), and sadly it is usually testing time that cops it.

This checklist should help you check you’ve got your main bases covered.

Tools you’ll need


  1. Having stable, valid code will save you a lot of debugging pain.  If you know your code is sound, then it is most likely a browser fault that is the cause of any further bugs from here onwards
    1. Run the W3C HTML validator on all pages and correct
    2. Run the W3C CSS validator on all pages and correct
    3. Run the WAI tool on all pages.
    4. Run your JavaScript code through JSLint
    5. If you are hoping to have good coverage on mobiles, run mobileOK on your pages
  2. Check the static text content for spelling and grammar.  Mistakes here will just make you look stupid and unprofessional.
  3. Check you’ve done a full run-through of the site in each supported browser.  You do have an agreed browser matrix don’t you?
  4. View your site without CSS, can you still read everything?
  5. Open up each template in a new tab and run through them using CTRL+Tab to check your layouts are all the same – do the edges of the main layout boxes sit at the same position on each page or do they jump a few pixels? Is the main heading at the same position (presuming your design intends that)?
  6. Run through all pages at 1024×768 resolution (or 800×600 if you are required to support it), do you have to scroll horizontally?
  7. Try using your site without JavaScript.   If you can’t then non-JS users will not be able to use your site and search bots will not be able to index all your content
  8. Have you remembered your print stylesheet? Print preview your pages and actually print the most likely to be printed (e.g. an article template).  Sometimes the preview lies.
  9. If you are using flash, check your non-flash fall-back works.  You do have a fall-back right?
  10. If you are using embedded media, check there is a fall-back for those without it and a link to the vendor site to download the plugin.
  11. If you have static links in the site, run a linkchecker.  I’ve used Xenu in the past which is fairly good, and free.
  12. Run your pages through YSlow to find any major issues
  13. If you are using XMLHttpRequest (often referred to as AJAX), test what happens when the server returns a failure (e.g. a 404 or 500 rather than 200 OK)
  14. Accessibility checklist (this isn’t a definitive list, if for example your navigation is inconsistent between pages then you’ve a problem that requires the team going back to the drawing board to solve)
    1. Your content is in a sensible order in the HTML.  If you are not sure, looking at the page with CSS off will help you decide.
      • e.g. you do not break up your main content paragraphs to line up your right hand promo boxes
    2. Go through a few pages with a screenreader and keyboard.
      • Can you get to all your links and form fields without the mouse?
      • Can you hear all your content?
      • Is hidden content still ‘perceivable’?  If you are hiding with display none or visibility hidden, screenreaders will ignore that content, but if you use position absolute with left being a large negative number, it will still get read out to the user.
    3. Test all your interactive features (e.g. accordions or sliders) with a keyboard, also is the screenreader able to read the information in them? (see my previous post about focus)
    4. Page sections are marked out with headings
    5. Headings do not skip levels.  This should be picked up in the HTML validation.
    6. Have a ‘skip to content’ link at the top of your page
    7. You can pause any animation or movement that is on by default
    8. All forms have a submit button
      • that especially includes select field (dropdown) navigation, it should never be executed from the onchange or onselect events
    9. Text can be resized in all browsers, IE6 does it differently so be sure to test if you support it
    10. All form fields have explicit labels (i.e. you use the for attribute to refer to the field’s ID rather than wrapping the field inside the label element)
    11. Missing alt attributes is a common mistake, but will be picked up in your HTML validation
    12. Foreground and background colours have sufficient contrast
    13. Testing for seizure-causing animation is difficult, but the safest course of action is not to flash anything more than 3 times in 1 second.
    14. Image maps have their links duplicated underneath as a list
    15. Any static links have sensible link text that indicates where the link will go on its own without the rest of the paragraph text around it.
    16. Any linked images have sensible alt text that indicates on its own where the link will go
    17. Specify what language the page is in.  The HTML validator will pick it up if it is missing.
    18. Tables of data have a summary and scope for each column and each row is defined

Get some focus: keyboard and mouse events

When we add interactivity, we use event handling.  A common trap is to handle the events that are fired by the mouse but forget about what non-mouse users need.


Click events are obviously fired from the mouse clicking on an item, but they are also fired when the user is focussed on an element and presses enter, so click events are covered for keyboard users as long as they can tab onto them in the first place.

When you click on something, a focus event is fired as well as the click event, after all you are focussing on something when you click on it.  If you are listening for the focus event and attaching functionality to it firing, remember that this functionality will also occur when you click on the attached elements.


Mouseout and mouseover events rely on a mouse to trigger them

For mouseout events, pair them with blur events

  • myelem.mouseout = function(){etc.}
    myelem.blur = myelem.mouseout;

For mouseover events, pair them with focus events but remember they will trigger on mouse click as well as mouseover

  • myelem.mouseover = function(){etc.}
    myelem.focus = myelem.mouseover;

See the code examples

(If you want to find out more about events, PPK has an excellent events write-up)

Get some focus: outline

When a link or button has focus, there is usually a visual indicator such as a dotted or coloured line around the element.

The CSS property is called outline.

Some designers/developers find the outline ugly and remove it using outline: none. Some reset CSS files also include this by default, with the optimistic expectation that you will set your own.

Eric Meyer says that he’s removed the outline “so that you remember to define your own”, however I’ve noticed that this is the exception rather than the rule.  I’ve a great deal of admiration for Eric Meyer, his work on CSS has improved many a developer including me, but on this issue I have to disagree.  A quick search on google code search shows just how regularly it doesn’t get done.

If you really wanted people to remember to redefine their own outline styles, you’d make it 6px solid red.

Removing the outline makes a page inaccessible because a keyboard-based user has no indication of what has focus or where their cursor is.  They have absolutely no idea of where they will end up if they press the enter key and absolutely no idea of where they are in the page order.

Visible focus in now an AA level accessibility guideline: 2.4.7

So please don’t remove the outline, and if you come across

outline: 0;

in your CSS, remove this and set the outline to 0 on the hover and active states  or reset it using the :focus pseudo-class:

:focus {outline: 1px dotted;} /* or similar */

The colour of the outline should be inherited from the link’s default colour if you don’t specify one.

UPDATE: Patrick Lauke also has an interest in this and has set up his own CSS outlines test page. Suppressing the outline on the :active pseudoclass seems to be the most elegant solution to stay accessible while avoiding the outline for mouse users.

UPDATE 2: Patrick has since discovered a flash of outline occurs on links to external pages so the best suppression technique is:

a:hover, a:active { outline: none; }

Get some focus

When you click on or tab to an element, a ‘focus’ event is fired.  Focus has a significant role if we want to make sure our websites and especially our interactive features are accessible to everyone.

Focus is by default indicated visually either using an outline around the active element or by the flashing cursor for typed input areas.  This indicator may vary between browsers.

Chrome Browser showing focus

Chrome Browser shows focus by having a orange line around the link

Firefox link showing focus

Firefox shows focus by having a dotted line around the link

IE link showing focus

IE shows focus by having a black dotted line around the link

However, there are only 2 types of element that can receive focus through the keyboard.

  • anchor elements with href attribute
    • <a href=”#”>…</a>
  • form field, for example
    • <input type=”text” /> and other input elements
    • <textarea></textarea>
    • <select>…</select>
    • <button>…</button>

Wherever you need to have user-triggered interactivity, you need to use one of these focusable elements for the trigger.  I’ll cover this in greater depth in a later post.

About tabindex

Yes, you can force elements to receive focus by giving them a tabindex.   However, you then mess up the order in which elements receive focus.  Instead of following the order in which the HTML was developed, the focus will run through the tabindexed elements by numerical order, then go onto the non-tabindexed elements.  This might result in the cursor jumping around the page, which is especially annoying if the page is long enough to scroll.

Try the code example to test it out