Progressive Enhancement

This article was first published on (08-24-2014).

In Designing an Effective Donate Form Brad Frost recommends using buttons in lieu of radio buttons, citing a tweet from Luke Wroblewski. I disagreed and Brad responded saying he had a different point of view. After more people got involved the demo was updated to use radio buttons, but I think there is still room for improvement; I intend to demonstrate that Plain Old Semantic HTML (POSH) + CSS can take us pretty far.


How to mark up and style a form like the one below (original):

animated gif showing the form UI

Image from bradfrostweb

Check the demo page

The basics

The primary goal should be to build the form using semantic markup, keeping progressive enhancement in mind. Submitting the form and accessing its content should not require JavaScript.

Original Markup

Original example (pseudo-code):

        <li><button data-message="...">$25</button></li>
        <li><button data-message="...">$50</button></li>
        <li><button data-message="...">$100</button></li>
        <li><button data-message="...">$250</button></li>
        <li><button data-message="...">$500</button></li>
        <li><input type="text" value=""></li>
    <input type="checkbox" /><label></label>

The heading

In the updated version, the content of the heading is dynamically generated to match the value of the data-message attribute on the labels associated with the radio buttons (I assume it was the same with <button>).

The problem here is that these cues rely on JavaScript and are not exposed to screen-reader (SR) users.

Making this accessible to SR would require the use of ARIA live regions:

Live regions inform assistive technology users of updates in the document without the user losing focus from their current activity.

WAI-ARIA Live Regions Updated

Also, SR users can navigate through headings, therefore changing the content of headings may lead to confusion.

As a side note, since each “step” of the donation process is wrapped inside a fieldset., it may be approapriate to use legend rather than a heading (even though this can be either “noisy” or useless for SR users).

The fieldsets

A fieldset should have a legend element:

It is required that the fieldset and legend are used in conjunction. A fieldset cannot be used without a legend and visa versa.

Fieldsets legends and screen-readers

The first element inside the fieldset must be a legend element, which provides a label or description for the group.

H71: Providing a description for groups of form controls

This is tricky though, because I must admit that even though my example includes a legend, it is styled with display:none. This is because the legend sounds very redundant in the context of that small form; moreover, Voice Over (VO) seems to announce the legend after the labels.

Update: the legend is still hidden, but I am now applying aria-labelledby to the fieldset. It is tied to the heading.

The list

I don’t see the need for a list here. Maybe it is somehow relevant to the use of buttons versus radio buttons(?), because radio buttons imply grouping - unlike a bunch of buttons.

I don’t think it hurts, but because it is irrelevant, it is also unnecessary.

The buttons

Buttons mean more tab stops for keyboard users and require JavaScript to submit data. Radio buttons are a much better fit because they are made for this (single option).

The @type

Replacing type="text" with type="number" on the “other amount” field gives us client-side validation for free (\w00t/).

The placeholder

@placeholder is not a replacement for label. There is no label associated with the “other amount” field and I do not see a @title either (see H65).


With Plain Old Semantic HTML, the markup could look like this:

            <input type="radio" />
            <label><b>$25 ...</b></label>
            <input type="radio" />
            <label><b>$50 ...</b></label>
            <input type="radio" />
            <label><b>$100 ...</b></label>
            <input type="radio" />
            <label><b>$250 ...</b></label>
            <input type="radio" />
            <label><b>$500 ...</b></label>
        <input type="number" value=""><label></label>
        <p><input type="checkbox" /><label></label></p>

Check the page without CSS

No more heading, no more list, and no more use of @data-message. The value of that attribute is now part of the content, which is conveyed to SR users as they navigate through the form controls.

With this basic markup, users can make a selection and submit the form without the need for JavaScript. Thanks to type="number", we can even validate user input.

I kept the “monthly donation” input/label near the bottom of the form, but I like Toby Marsden’s suggestion to move it up, before all the options.

The enhancement part

Now that we have a functional form, we can toy with it using CSS. We can use :focus/:hover with the adjacent sibling combinator (+) to popup the content of the labels, to slide a “marker” (what tags the selection) across labels, to generate content, to style the shadow DOM, etc.:

/* to popup the label of the selected radio button */
input:checked + label > b {
    display: block;
/* to style the generated text associated to the selected radio button or when the cursor is over the label */
input:checked + label:before,
label:hover:before {
    font-size: 1em;
    text-shadow: none;
/* to remove the "spin" button (which allows to increment the value) */
::-webkit-inner-spin-button {
    -webkit-appearance: none;
/* to make the placeholder text fades out when the field gets focus */
#other-amount:focus::-webkit-input-placeholder {
    opacity: 0;
    transition: opacity .3s;
/* check the <style> block from the demo page */

This is the end result

Other considerations

From there, we would…

Related article: developing an Accessible star ratings widget.