Buttons are ubiquitous in websites and application interfaces—to submit data and initiate actions in toolbars, forms, and navigation controls—and designers frequently customize their appearance. We typically design custom-styled buttons for projects that must:
• Match the branding and presentation of the overall site or application
• Use color, texture, and dimension to convey button hierarchy—for instance, visually distinguishing primary and secondary actions
• Include icons to make them more uniquely identifiable and easier to scan, or compact, icon-only buttons, like those used in text-editing toolbars
• Introduce custom interaction states to provide better feedback, like a pressed state for touch-screen buttons
Virtually any HTML element can be made to look and behave like a button with a bit of CSS and JavaScript—for example, Google’s Gmail web application uses div
-based buttons throughout the interface. But relying on JavaScript events to handle form submission is risky; when scripting is disabled or not fully supported, all related functionality is rendered unusable.
For foundation markup that reliably works everywhere, we need to start with either an input
element with a type
of button
, or the button
element. Which of these elements to choose depends on the styling and functionality needs of the particular situation.
In this chapter, we’ll walk through two target designs and discuss the criteria for deciding when to use input
or button
elements in the foundation markup, and then review how to transform them into richly formatted buttons in the enhanced experience.
Let’s say we have a social networking site with a page that lists friend invitations, which can be accepted or ignored. Our design introduces a visual hierarchy for the buttons that emphasizes the positive Add Friend action over the negative Ignore action. Let’s consider two possible target designs that illustrate the key factors that will help us decide which native element to use: a simpler one with buttons styled distinctly to show a difference in emphasis:
...or a more complex design that calls for icons inside each button and mixed font weights for the text formatting (the Add John is bold):
There are two native elements we could use to code the foundation markup for our custom buttons: an input
element with its type
attribute set to submit
, reset
, or image
, or a button
element. Of the two markup options, the input
element has been part of the HTML specification longer than button
(input
was included in the HTML3 specification circa 1996, vs. the button
’s inclusion in the HTML4 specification around late 1999), and tends to work correctly in a broader range of older and mobile browsers. Because it’s universally functional, an input
is our first choice for creating buttons in the foundation markup.
Designers have traditionally avoided using standard input
-based buttons when a design calls for a styled button; there’s a common misperception that the input
can’t be styled reliably with CSS. We tested this idea and were surprised to find it’s possible to consistently style input
-based buttons in all mainstream browsers (even Internet Explorer 5!) with various type styles, background images, borders, rounded corners, custom dimensions, margins, and padding.
There is, however, a key limitation with an input
: this element consists of a single self-closing tag, so it can’t contain another HTML element—like an image, span
, em
, strong
, or any other HTML tag. This prevents us from using the input
for complex text formatting, layering several background images, or adding multiple icons.
The button
element, by contrast, has both opening and closing tags and can contain additional HTML elements. This allows us to accommodate all the design features of our more complex target design—we can wrap the Add John text in a strong
tag for emphasis and add a span
tag to apply the icon. However, the button
has its own drawback: since it was absent from the earliest HTML specifications, some older browsers and mobile platforms don’t recognize it, and will either fail to display the button at all or won’t properly submit the form when it’s clicked.
For this reason, we recommend that our foundation button markup always be an input
, and that we use button
elements only in the enhanced experience. For the first, simpler design, we’ll style the input
for the enhanced experience as we would any other element; for the more complex design with formatted text and icons, we’ll “transform” the input
into a button
to achieve advanced formatting and retain the ability to natively submit form data.
We’ll start by reviewing how to style an input
-based button. Then, we’ll walk through how to transform an input
into a button
element in the enhanced experience to accommodate the additional icons and text formatting of the second example.
We’ll construct our first design with input
-based buttons and style them with CSS for both the basic and enhanced experiences. The design (as shown in Figure 14-1 above) is simple enough for this approach to work in a wide range of browsers.
To apply a visual hierarchy between the primary and secondary buttons, we’ll distinguish them in the markup with descriptive classes assigned to the input
elements (btn-primary
and btn-secondary
) in the foundation markup. To be safe, we’ll reserve much of the visual styling, such as the light-on-dark text of the Add button, for the enhanced experience, since older browsers may support styling only the text color (and not the background image or color), which may result in unreadable text.
We can, however, show visual emphasis in the basic experience by setting the font weight of the primary button to bold. (Though this style property is generally well supported, it may be ignored by some browsers when applied to an input
.) In even very primitive browsers, our hint of primary and alternate actions will be communicated in the basic experience (as shown in Figure 14-2 above).
The foundation markup for our social networking site starts with a form
tag; within the form is a fieldset
that contains a legend
, a paragraph with the location and count of common friends, and two submit input
elements for adding and ignoring friend requests:
We’ll add a class to each input
element to identify its role as either primary or secondary:
Our foundation markup now provides a usable experience on any device that supports basic HTML:
The form is perfectly usable, but could use some visual polish, so we’ll add a few safe CSS rules to the basic style sheet.
One style property that has a big impact on look and feel is font-family
. Most elements on a page inherit font-family
properties from their parent elements, but form controls are often an exception; many browsers specify a monospaced font for form controls regardless of other font styles set on the page. To fix this, we’ll point our font-family
rule at the body
and input
elements:
body, input { font-family: "Segoe UI", Arial, sans-serif; }
This simple rule makes our form look a lot better already!
To improve the form’s legibility, we’ll remove the fieldset
’s border and adjust its margins, style the legend
to be bold and green, and adjust the margins on the paragraph inside:
Now the form block in the basic experience looks even closer to our target design:
Finally, we’ll style the primary button with bold text and specify that the secondary button should have normal (not bold) text, to ensure a shift in visual emphasis in the basic experience:
.btn-primary { font-weight: bold; }
.btn-secondary { font-weight: normal; }
With these styles in place, the Add button takes visual precedence:
Now that our foundation markup and styles are complete, we’ll tackle the enhanced markup and styles that are applied to the page when the browser passes the capabilities test.
The buttons in our target design share a number of styles, so in our enhanced style sheet we’ll write a single rule against both button selectors to apply shared styles. These rules define padding, font formatting and alignment, and set the cursor style to be the clickable hand icon over the input
:
The buttons’ font weight is inherited from the primary and secondary classes we wrote into the basic style sheet, so they don’t need to be defined again in the enhanced style sheet.
When setting button styles, using em
units instead of pixels for properties like padding and margin, to ensure that the button (and the space it occupies) will expand and contract relative to its font size.
Older versions of Internet Explorer add extra width to input
and button
elements that can’t be set with padding
or margin
values, but there is a fix: specify a width
property and set the overflow
property to visible
. If the button doesn’t require a specific width
, you can set the value to auto
:
To give the buttons rounded corners, we’ll apply the CSS3 border-radius
property. At the time of this writing, only Mozilla- and Webkit-based browsers support the border-radius
property (using vendor-specific property names), but it’s still safe to use, because browsers that don’t support it will simply display the buttons with square corners. We’ll also append the standard border-radius
property specified by the W3C, so that any browsers that implement this feature in the future will show rounded corners as well (both Opera and Internet Explorer have committed to supporting this property at the time of writing):
The primary and secondary button classes in the basic styles specify background images (green and silver, respectively), and text and border colors to match the design:
To help the text stand out against the background image, the text-shadow
property adds a drop shadow on the button text. Like the border-radius
property, text-shadow
is supported by only a handful of browsers at this time (specifically, Firefox 3.5+, Safari 1.1+, and Opera 9.5+), but is harmless to include as long as the text has good contrast without the shadow.
To provide good visual feedback when the user interacts with the button, a hover state is defined for each button type. Ideally, we would do this using the :hover
pseudo-class, but Internet Explorer versions 6 and earlier don’t support this on elements other than anchors (a
). To remedy this, we’ll use JavaScript to add and remove a hover class when the user moves the cursor onto or off of a button:
Lastly, we’ll add font-size rules to the legend
and paragraph to create appropriate visual weight for each:
legend { font-size:1.5em; }
fieldset p { font-size:1.3em; }
With these styles applied, the enhanced markup looks exactly like our target design:
The scripting required for the input
-based button is minimal: we’ll use JavaScript to apply the hover
class to the buttons when the user places their cursor over a button. This approach ensures that proper hover-state feedback will be displayed in all browsers that pass the capabilities test.
To start, we’ll create two variables that capture our input
elements, one each for all of the primary and secondary buttons on the page:
Then we’ll add the hover
class by binding a function to the input’s mouseover
event, and remove it using the mouseout
event. As a convenience, jQuery provides a single hover
method that lets you pass in functions for both the mouseover
and mouseout
events. The following example shows how to apply this hover-state toggle to primary buttons:
For secondary buttons, we’d construct a similar hover
method that acts on the secondaryButtons
variable, and use it to toggle the btn-secondary-hover
class on and off.
Our input
buttons now support the behavior and visual appearance specified in our initial target design. Next, we’ll review how buttons with more complex visual formatting can be transformed from an input
into a button
in the enhanced experience.
Our second target design builds on the first example, with a couple of additional features: icons for the Add and Ignore action buttons, and varying font weights to emphasize a portion of the primary button text.
In this case we can’t style the input
element to match the target design, so we’ll use an input
element in the foundation markup, which we know will work reliably in all browsers and devices. In the enhanced experience, we’ll run a script that replaces the input
with a styled button
element for a richer experience.
When swapping the input
element with the button
, we’ll copy all of the input
element’s attributes and JavaScript events over to the button
to ensure that the new element is a one-to-one match. The new button
will completely replace all of the functionality of the input
element, so when the script is complete, we’ll remove the input
from the page entirely.
Like the first example, we’ll start with input
elements and assign primary and secondary classes, but we’ll add an additional class to each input
to identify the purpose of the buttons (action-add
and action-ignore
). These classes will be transferred to the button
elements in the enhanced experience to apply specific icons to the buttons:
To identify the button text that should be styled with bold, we’ll use an HMTL character as a delimiter; the script will look for this delimiter and apply the bold style to the words that precede it. A good choice for a delimiter is a punctuation mark or special character that makes sense when displayed in the basic experience and is unique enough to be detected by the script. In this button, there isn’t a visible character that would read correctly, so the best choice for a delimiter is a non-breaking space character (
), which is invisible in the basic experience:
The basic styles we created for the first example also apply here, with one slight modification: we’ll add button
to the font-family
selector so that it inherits the correct font style:
body, input, button { font-family: "Segoe UI", Arial, sans-serif; }
With foundation markup and basic styles in place, we’ll tackle the enhanced markup and styles that are applied to the page when the browser passes the capabilities test.
When the browser passes the capabilities test, we’ll use JavaScript to replace each input
with a button
element that has the same attributes and JavaScript events:
Our enhancement script will remove the delimiter we inserted in the foundation markup—a non-breaking space (
)—and wrap the text that precedes it in a strong
element:
To apply an icon, the script will insert a span
element with a class
of icon
to the markup, before the button
text:
As we did with the basic CSS, we’ll build upon the enhanced CSS we created to style an input
to specify styles required for the enhanced button
element.
In our target design, a portion of the primary button’s text is bold, while the rest is normal font weight. The btn-primary
class applies a bold font-weight
property to all primary buttons, so we’ll need to make an exception for this particular primary button. We’ll write a new style rule against the action-add
class, specifically for the primary button in our target design; that way other primary buttons in the page will remain bold:
button.action-add { font-weight: normal; }
The input-to-button script will wrap strong
tags around the emphasized text, Add John. By default, most browsers apply a bold font style to strong
elements, but to ensure that the proper formatting is applied, we’ll reinforce this style with a simple rule:
button strong { font-weight: bold; }
We’ll also add styles for the icon span
s that apply an inline-block
property and dimension:
Finally, we’ll set a background image for each icon span
, based on the parent element’s class:
Now that our CSS supports complex button formatting, we’ll move on to scripting the input-to-button conversion.
Our goal is to replace specific input
elements in the foundation markup with custom-styled button
elements when a browser passes the capabilities test. To start, we’ll create a buttoninputs
variable to store an array of references to all of the button-like input
elements on the page:
var buttoninputs = $('input.btn-primary, input.btn-secondary'),
We’ll loop through each item in the array using jQuery’s each
method and replace it with a new button
element, including all of the input
element’s attributes and event bindings. The each
method accepts an argument—a function—to execute on each item in the buttoninputs
array:
In this function, we’ll write the logic that grabs all of the input’s attributes and events, assigns them to a new button
element, and replaces the input
.
First, we’ll create a variable that acts as a template for the button
markup, and use the input
element’s value for the button
element’s text label:
Within the function we pass to the each
method, the this
keyword refers to the current input
element. By wrapping this
in parentheses and preceding it with a dollar sign, we turn it into a jQuery object reference, which allows us to apply jQuery methods to manipulate the element. For example, the statement $(this).val();
returns the value of the input.
Next, we’ll get all of the input
element’s attribute name/value pairs (nodeName
and nodeValue
, technically) and apply them to the new button
using the setAttribute
method:
At the time of writing, jQuery’s attr
method causes an error when it’s used to set the type
attribute in Internet Explorer 8. To avoid a conflict with Internet Explorer, we used the native JavaScript setAttribute
method rather than jQuery’s shortcut attr
method; as with all native JavaScript attributes, setAttribute
must reference the button
variable using array notation, button[0]
.
Now that we’ve reassigned the input
element’s attributes to the button
, we’ll do the same for all JavaScript events bound to the input
element. Events bound using jQuery are stored in memory via the data
method, which can be used to both get and set data values. We’ll pass a single argument to that method—in this case, events
—to retrieve all of the events tied to our input. And because each event, like onload
or onclick
, can have more than one binding, or function, associated with it, we’ll also loop through those bindings to make sure we get them all:
At this point, we’ve created custom buttons using the original input
elements’ attributes and events. Now we can safely remove the input
elements from the markup. jQuery’s replaceWith
method gives us a tidy way to remove the input
element and insert the new button
element simultaneously:
We’ve now converted the input
s to button
elements; next, we’ll add the text formatting (strong
tags) and icon spans. We’ll reset the inner HTML of the action-add
button by splitting its text at the non-breaking space character, and wrapping the first portion in a strong
element:
Finally, we’ll add the icon span
elements to all buttons using jQuery’s prepend
method, which inserts HTML at the beginning of an element:
$('button').prepend('<span class="icon"></span>'),
The code that accompanies this book (available at www.filamentgroup/dwpe) includes a jQuery plugin that packages the principles shown in this chapter—specifically, in the second example—into a reusable script for use in your projects. To use the code, simply include all the files referenced in the button demo page, and call the inputToButton
method on any input
elements you’d like to convert to a button
.
$('input#submitForm').inputToButton();
The inputToButton
plugin will automatically transfer over any classes and events and apply hover classes to the buttons. You can safely call the inputToButton
method on all input
elements, as the script will make sure to not convert any non-button input types, like text, radio
, or checkbox
.
$('input').inputToButton();
To add the icon spans and non-breaking space-delimited text bolding (as we did for the Add button in this chapter), you can direct additional logic to look for the non-breaking space and inject the icon span
:
In addition to submitting data, buttons are commonly used in applications as toggles for settings or preferences. Toggle buttons typically have an additional “pressed” state to make it appear selected.
Toggle buttons can either act alone, as a single on/off switch, or as part of a set that allows one selection at a time. If you think this sounds similar to another kind of HTML element, you’re right—toggle buttons behave exactly like checkbox and radio button inputs.
For a detailed example of how to style checkbox and radio inputs like toggle buttons, check out Chapter 15, “Checkboxes, radio buttons, and star rating.” The code that accompanies this book also includes a demo page with toggle buttons that resemble those shown in this chapter. Check out toggle-buttons.html
in the accompanying code’s checkbox-radiobutton
folder.
* * *
Custom-styled buttons can deliver enhanced visual sophistication and improve the usability of a site. When creating your own buttons, keep the following rules in mind to ensure a usable experience for all:
• Start with a universally functional input
element with minimal, safe styles to ensure that all users have access to key functionality.
• For simpler button designs (without text formatting or icons), use the input
element for both the basic and enhanced experience, and apply enhanced styles when the capabilities test passes.
• For more complex designs, convert each input
element to a button
to add style and visual feedback for capable browsers and devices.
• Style the button font size and padding with em
s rather than pixels to preserve text-size scaling.
• To minimize server requests, use image sprites for button backgrounds. Just be sure that the image sprite is wide enough to accommodate your longest button at an enlarged font size.
13.59.180.145