Much of the Salesforce user interface is accessible out of the box to users who require assistive technologies. Here's how can you ensure that your custom Visualforce or Lightning pages also have the best possible experience for everyone -- even users who are blind or have vision impairments, or have impaired motor control and can't use a mouse.
2. Hello! We’ll be your presenters today.
Jesse Hausler
Principal Accessibility Specialist, Salesforce
@jessehausler
Shannon Hale
Director of UX, Skuid
(+ former Accessibility Workgroup member @ Salesforce)
@shannonsans
3. Defining web accessibility
Accessibility enables people with disabilities to perceive, understand,
navigate, interact with and contribute to the Web.
● Vision
● Mobility impairments
● Deafness and hard of hearing
● Cognitive
11. Forms
The most common and important
task in a CRM is entering data, so
accessible forms are critical.
12. Form labels
When labels aren’t programmatically
associated to inputs, assistive technology
users have no information about what to
type in each field.
13. Form labels
When labels aren’t programmatically
associated to inputs, assistive technology
users have no information about what to
type in each field.
14. <label/> tags associate inputs with their purpose
All <input/>, <textarea/> and <select/> HTML elements need labels
● Match the for attribute of <label/> tags to the id attribute of the
corresponding input control
● Remove orphaned labels (<label/> without an associated input control)
15. <label/> tags, great for mouse users too!
Click on a label to:
● Place focus inside of an input field or textarea
● Open a picklist
● Check a checkbox
● Select a radio button
17. Labels in Visualforce
Salesforce Classic Layout/Styling
● For fields on a Salesforce object, <apex:inputField/> automatically
associates labels and input controls as a child of <apex:
pageBlockSection/>
● For fields not on a Salesforce object, use <apex:outputLabel/> with
<apex:input/>, <apex:inputText/>, etc. inside <apex:
pageBlockSectionItem/> and match the for attribute of <apex:
outputLabel/> with the id attribute of <apex:input*/>
Custom Layout/Styling
● For Bootstrap or other custom layouts, use <apex:outputLabel/> with
<apex:inputField/>, <apex:input/> etc. as above
22. But what happened to the ‘required’ indicator?
<div class="form-group">
<apex:outputLabel for="LastName" value="{!$ObjectType.Contact.fields.LastName.Label} *"
styleClass=”control-label” />
<apex:inputField id="LastName" value="{!contact.lastName}" required="true" styleClass="form-
control" />
</div>
We set required=”true” on <apex:
input/> but Bootstrap doesn’t
know what to do with this.
Provide a text indicator such as
an asterisk on the label, so it’s
accessible to assistive tech.
23. A note about required fields
The Salesforce Classic red bar or other styling on its own is not enough to
indicate a required field.
If you’re rolling your own page markup, make sure to provide some text
indication of field requiredness inside the <label> tags.
What you don’t see here is that Visualforce- and Salesforce
Classic-generated required field labels have an asterisk in the
label that’s visually hidden, but still available to assistive
technology.
<label for="j_id0:j_id1:j_id2:j_id5:j_id6">
<span class="assistiveText">*</span>
Last Name
</label>
26. Labels in other frameworks
In other frameworks (React, Angular, SLDS outside of Lightning, etc.), always use
<label/> with <input/>, <textarea/> and <select/> HTML elements.
<div ng-controller="ExampleController">
<form novalidate class="simple-form">
<label for="name">Last Name *</label>
<input type="text" id="name" ng-model="contact.lastname" required /><br />
<label for="numGuests">Number of Guests</label>
<input type="number" id="numGuests" ng-model="contact.numberOfGuests" /><br />
<input type="checkbox" id="okToContact" ng-model="contact.okToContact"
value="okToContact" />
<label for="okToContact">Contact with special offers</label><br />
<input type="submit" ng-click="update(contact)" value="Save" />
</form>
</div>
27. Hiding labels (or other content) visually on a page
Sometimes you want to visually hide a label, heading or other content while still
making the label available to assistive technologies.
Don’t use display: none or visibility: hidden in the CSS. Both of
these will hide the content from assistive technologies -- use them only when you
want to hide the content from EVERYONE.
To do it right, add a class to the element you want hidden:
● Lightning Design System: slds-assistive-text
● Bootstrap: sr-only
● Visualforce and other frameworks: create a custom class (see next slide)
28. Hiding content visually on a page with custom CSS
If you’re using another framework or rolling your own styles in Visualforce,
create a custom class and add it to the content you want to hide:
.assistive-text {
position: absolute;
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
clip: rect(1px, 1px, 1px, 1px);
padding: 0;
border: 0;
height: 1px;
width: 1px;
overflow: hidden;
z-index: -1000;
}
30. aria-describedby associates
help text and error messages with input controls
While <label> tags are used to associate form fields with their label, aria-
describedby is used to associate information beyond a label to an input
● When a user places focus on an input, both the label and the help or error
text will be available to screen readers
31. Using aria-describedby in Visualforce
● Visualforce <apex:input*/> tags don’t have native aria-* attribute
● Leverage HTML pass through attributes and add html-aria-describedby
● Use the $Component global merge field to get the generated id of the
related element if it’s an <apex:outputPanel/> or other Visualforce
component
Unfortunately, neither Salesforce Classic or Visualforce set aria-describedby automatically for
errors, so this is a manual exercise. If the user has Accessibility Mode enabled, then labels of fields with
errors prepend “Error -” to the label. Lightning handles this better, as you’ll see.
32. Visualforce aria-describedby example
Visible inline help text is great for settings like this one in the
Nonprofit Starter Pack, which aren’t edited very frequently and
can be hard to understand from just the label.
33. Visualforce aria-describedby example
STG_PanelContacts: right now, the help text isn’t semantically associated with the field it describes
<div class="form-group">
<apex:outputLabel value="{!$ObjectType.npe01__Contacts_And_Orgs_Settings__c.Fields.
npe01__Account_Processor__c.Label}" for="slAP" styleClass="col-sm-4 control-label" />
<div class="col-sm-8 form-control-column">
<apex:outputField value="{!stgService.stgCon.npe01__Account_Processor__c}" rendered="{!
isReadOnlyMode}" />
<apex:selectList value="{!stgService.stgCon.npe01__Account_Processor__c}" multiselect="
false" size="1" rendered="{!isEditMode}" id="slAP" styleClass="form-control">
<apex:selectOptions value="{!listSOAccountModels}"/>
</apex:selectList>
</div>
<div class="col-sm-offset-4 col-sm-8 help-block">
<apex:outputText value="{!$Label.stgHelpAccountModel}" />
</div>
</div>
35. Errors and aria-describedby in Lightning
Set errors on a field using the component’s .errors attribute to dynamically
create and insert the error message and aria-describedby tag into the field
DOM:
createExpense : function(component, event, helper) {
var amtField = component.find("amount");
var amt = amtField.get("v.value");
if (isNaN(amt) || amt==''){
amtField.set("v.errors", [{message:"Enter an expense amount."}]);
}
else {
amtField.set("v.errors", null);
var newExpense = component.get("v.newExpense");
helper.createExpense(component, newExpense);
}
}
More on error handling: https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/js_throw_error.htm
36. Errors and aria-describedby in Lightning
This is what the generated markup looks like from the previous example:
<div class="slds-form-element slds-is-required">
<div class="slds-form-element__control">
<div class="uiInput uiInputNumber uiInput--default uiInput--input has-error">
<label class="slds-form-element__label uiLabel-left form-element__label uiLabel" for="
34:2;a" ><span class="">Amount</span><span class="required">*</span></label>
<input class="slds-input input" max="99999999999999" step="1" type="text" min="
-99999999999999" aria-describedby="112:c" placeholder="" required="" id="34:2;a">
</div>
<ul class="has-error uiInputDefaultError uiInput uiInputNumber uiInput--default uiInput--
input" id="112:c">
<li class="form-element__help">Enter an expense amount.</li>
</ul>
</div>
</div>
37. Help and aria-describedby in Lightning
<div class="slds-form-element slds-is-required">
<div class="slds-form-element__control">
<ui:inputText aura:id="expname" label="Expense Name" class="slds-input"
labelClass="slds-form-element__label" value="{!v.newExpense.Name}"
required="true" />
</div>
<div class="custom-help-block">
<p>Please use the format: Date - last name - purpose</p>
</div>
</div>
Unfortunately, the ariaDescribedBy attribute hasn’t been surfaced in the Lightning Component
framework on the platform as of Winter ‘16 (more info)
38. Using aria-describedby in other frameworks
In other frameworks, just use the aria-describedby attribute on the
<input/> element to associate it with the element containing the message. You
can use Element.setAttribute() to set the attribute dynamically via
JavaScript during error handling.
<label for=”myFieldId”>Field Label</label>
<input id=”myFieldId” type=”text” value=”fieldValue” aria-
describedby=”myMessageId” />
<span id=”myMessageId”>More information or an error message
about this field</span>
39. Group related radio buttons within a <fieldset/>
Consider that a set of radio buttons are an answer to a question.
40. Group related radio buttons within a <fieldset/>
<label> associates answer choices to their radio buttons.
<input type=”radio” id=”color-blue” name=”color” value=”blue” />
<label for=”color-blue”>Blue</label>
41. Group related radio buttons within a <fieldset/>
The <legend/> of the <fieldset/> associates the question to the answer
choices.
<fieldset>
<legend>What is your favorite color?</legend>
<!-- blue/red/green/orange radio buttons go here -->
</fieldset>
42. Other use cases for <fieldset/>
● Group related fields -- consider
Shipping Address vs Billing Address
in compound fields
● Group checkboxes to allow multiple
possible answers (“check all that
apply”)
43. Using <fieldset/> in Visualforce
● Specify the legendText attribute on <apex:selectCheckboxes/> and
<apex:selectRadio/> to properly group the input controls in a fieldset
with a legend
● If needed, hide the legend from sighted users by setting
legendInvisible=”true” -- this adds class=”assistiveText” to the
<legend/> tag, which keeps to the legend accessible to assistive
technologies but hides it visually
44. Using <fieldset/> in Visualforce: Classic Styling
<apex:page controller="CheckboxTestControllerVF">
<apex:form>
<apex:selectCheckboxes layout="pageDirection"
legendText="Contacts to invite:"
value="{!contactIds}">
<apex:selectOptions value="{!items}" />
</apex:selectCheckboxes>
</apex:form>
</apex:page>
public List<SelectOption> getItems() {
List<SelectOption> options = new List<SelectOption>();
for (Contact c : [SELECT Id, Name FROM Contact]) {
options.add(new SelectOption(c.Id, c.Name));
}
return options;
}
45. Using <fieldset/> in Lightning with SLDS
<aura:component implements="force:appHostable" controller="CheckboxTestController">
<aura:attribute name="contacts" type="Contact[]" />
<aura:attribute name="checkedContacts" type="String[]" />
<aura:handler name="init" value="{!this}" action="{!c.doInit}" />
<div class="slds-form--stacked">
<fieldset class="slds-form-element">
<span class="slds-form-element__label slds-form-element__label-top">
<legend>Contacts to invite:</legend>
</span>
<div class="slds-form-element__control">
<aura:iteration var="con" items="{!v.contacts}">
<label for="{!'c' + con.Id}" class="slds-checkbox">
<input type="checkbox" id="{!'c' + con.Id}" value="{!con.Id}"
onchange="{!c.updateCheckboxes}" />
<span class="slds-checkbox--faux"></span>
<span class="slds-form-element__label">{!con.Name}</span>
</label>
</aura:iteration>
</div>
</fieldset>
</div>
</aura:component>
Note that we’re not using
<ui:inputCheckbox/>
here: as of Winter ‘16
there’s no way to (1)
position the label on the
right and (2) add the faux
checkbox <span/> for
SLDS styling.
46. Using <fieldset/> in other frameworks
<fieldset>
<legend>My Legend</legend>
<!-- Form fields and other grouped content go here -->
</fieldset>
47. If an element behaves like a button, it’s a <button/>
Generally speaking, if an element submits a form, executes some code without
leaving the page, or otherwise has button-like behavior, it should be a
<button/>
Image-only buttons have other
important considerations -- we’ll talk
about this in Images.
48. Why semantic <button/> use is important
● Buttons have different interaction behaviors than links and are announced
differently by assistive technologies
○ Pressing Space or Enter triggers onclick on button, but only Enter triggers onclick on
links
○ <a/> elements with an empty href attributes won’t receive keyboard focus
● <div/> and <span/> elements don’t receive keyboard focus, so aren’t
accessible from the keyboard*
● Some assistive technology uses shortcut keys to get a list of links or buttons
on the page -- you want the right elements to be returned
51. <img/> requires the presence of the alt attribute
The alt attribute helps assistive technologies describe the purpose of an image
to users who can’t see it
● Images that are purely decorative should have an empty alt attribute:
<img src=”decorative.jpg” alt=”” />
● Informational images should have a useful alt attribute value:
<img src=”exclamation.png” alt=”Warning” />
● “null”, “empty” and “undefined” are not appropriate alt values
● If the alt attribute is missing, the file name will be read instead!
52. alt attributes in Visualforce
Use the alt attribute on <apex:image/> for accessible images in Visualforce:
<h1 class="h4">Recent Items</h1>
<ol class="list-unstyled recentItems">
<apex:repeat value="{!recentItems}" var="item">
<li>
<apex:outputLink value="/{!item.Id}">
<apex:image value="/s.gif"
styleClass="{!IF(item.type='Account', 'account24', 'contact24')}"
alt="{!IF(item.type='Account', 'Account', 'Contact')}" />
{!item.Name}
</apex:outputLink>
</li>
</apex:repeat>
</ol>
53. alt attributes in Lightning
<ui:image/> has an imageType attribute with two possible values:
● informational: the alt attribute is also required
● decorative: the alt attribute is not required
However!
<ui:image/> is not available in Lightning components as of Winter ‘16.
Instead, use the HTML <img/> tag and set the alt attribute as for any other
framework (remember to set alt=”” for descriptive images)
54. alt attributes in Lightning: SLDS Example
If you want to leverage the SVGs in SLDS inside Lightning
Components, you may need the Lightning SVG Icon
Component Helper
57. Image links and buttons need text content
With icon fonts, this is too common:
<a href="http://www.facebook.com/">
<span class="icon-facebook"></span>
</a>
<a href="http://twitter.com/">
<span class="icon-twitter"></span>
</a>
<a href="http://youtube.com/">
<span class="icon-youtube"></span>
</a>
<a href="http://plus.google.com/">
<span class="icon-googleplus"></span>
</a>
58. Image links and buttons need text content
How a screen reader announces this:
“Link”
“Link”
“Link”
“Link”
59. Image links and buttons need text content
How to fix it:
<a href="http://www.facebook.com/">
<span class="icon-facebook"><span class=”assistive-text”>Facebook</span></span>
</a>
<a href="http://twitter.com/">
<span class="icon-twitter"><span class=”assistive-text”>Twitter</span></span>
</a>
<a href="http://youtube.com/">
<span class="icon-youtube"><span class=”assistive-text”>YouTube</span></span>
</a>
<a href="http://plus.google.com/">
<span class="icon-googleplus"><span class=”assistive-text”>Google+</span></span>
</a>
60. Image links and buttons need text content
Bootstrap image example:
<button class="btn btn-link">
<span class="glyphicon glyphicon-remove delete-icon">
<span class="sr-only">Delete</span>
</span>
</button>
See also: http://salesforce.stackexchange.com/questions/99411/make-an-apeximage-appear-conditionally-with-apex-
code/99418#99418
62. Ensure interactive elements are accessible using
keyboard and assistive technology
These components all follow the W3C spec for accessibility
● Menu <ui:menu/>
● Modal dialog <ui:modal/>
● Non-modal panel <ui:panel/>
● Autocomplete typeahead <ui:autocomplete/>
● Tabset <ui:tabset/>
● Tooltip <ui:tooltip/>
Only <ui:menu> is supported in Lightning as of Winter ‘16 -- for a list of supported components, use
https://yourdomain.lightning.force.com/auradocs/reference.app rather than the open source http:
//documentation.auraframework.org/auradocs)
63. Use hyperlinks and buttons for click targets
Only <a/>, <button/>, and <input/> types should be:
○ Click targets
○ Hover targets
They natively receive and show focus, perform actions, and communicate with
assistive technologies.
64. Use hyperlinks and buttons for click targets
Don't make <div/>, <span/>
and other non-focusable
elements “clickable”.
65. Use hyperlinks and buttons for click targets
Navigation options:
● add onclick to a card for a
larger tap/click target, but
include a hyperlink to the
same destination
● wrap the card in an <a/>
Interaction:
● add an explicit <button/>
instead of onclick on a
non-focusable element
66. Using hover
● Behaviors which occur on mouse hover
must also occur with keyboard focus
● Triggers need to be focusable
In this example, the blue circle with the “i” character should
be an anchor. The tooltip shows when the anchor is in
focus.
67. Using hover
Don’t use hover to reveal actionable
content, such as buttons or links. Just
show them.
In Lightning Experience, pencils aren’t hidden behind
hover states. Instead, they get darker on hover.
68. Color Usage
Color is a key part of visual design
-- but not everyone using your page
will see it the same way you do
69. Text color contrast ratio
must meet the minimum requirement
According to the WCAG,
● Contrast ratio between text and background should be at least 4.5 to 1.
● If font is at least 24 px or 19 px bold, the minimum drops to 3 to 1.
For large text, the lightest gray you can use on white is
#959595
For small text, the lightest gray you can use on white is #767676
73. Other
● Have a descriptive page <title/>
○ This is good not only for assistive tech, but for those of us who like to have 20 tabs open in
our browsers!
● Properly nest your page headings (<h1/><h2/><h3/>)
○ Use heading tags for headings! <div class=”my-heading-style”>...</div> does
not communicate that the content is a heading to assistive technologies.
● Consider whether lists are ordered or unordered
● Data table cells must be associated with data table headers
● Have a descriptive title attribute for frame and iframe elements
● Avoid repetitive hyperlink text (Edit, View All, etc)
○ Provide disambiguation for assistive technologies by hiding the content visually
75. Testing and Debugging
● Unplug your mouse
● Test with users with disabilities
● Chrome Accessibility Developer Tools
● Mac OS X
○ VoiceOver - free screen reader software (System Preferences > Accessibility > VoiceOver)
○ Full Keyboard Access: All Controls (System Preferences > Keyboard > Shortcuts)
○ Safari accessibility: tab to links (Preferences > Advanced >Press Tab to highlight each item on
a webpage)
● Windows
○ NVDA - free screen reader software
○ Most screen reader users use JAWS on Windows