Input buttons must have discernible text
An `<input>` element with `type="button"`, `type="submit"`, or `type="reset"` does not have discernible text. Screen reader users cannot understand the button's purpose.
Rule Description
This rule ensures all input buttons have accessible text through the value, aria-label, or alt (for type="image") attributes.
What This Rule Checks
<input type="submit">has value or aria-label<input type="button">has value or aria-label<input type="reset">has value or aria-label<input type="image">has alt attribute
What This Rule Does Not Check
- Quality or clarity of button text
- Whether button text matches visual design
- Button functionality or behavior
<button>elements (separate rule)
Best Practices
- Use value attribute - Primary method for text
- Be specific - "Submit Application" not "Submit"
- Add context - Make purpose clear from label alone
- Keep concise - Short but descriptive
- Consider using
<button>- More flexible than<input>
Why It Matters
Impact on Users
- Screen reader users hear "button" without knowing what it does
- Voice control users cannot activate the button by name
- Users with cognitive disabilities need clear button labels
- All users benefit from clear button text for understanding and navigation
Real-World Scenario
A form has three input buttons: one to submit, one to save draft, and one to cancel. Without proper values, screen readers announce: "Button, Button, Button." Users cannot tell which button does what without visual inspection.
How to Fix
Solution 1: Use Value Attribute
Provide descriptive text in the value attribute.
Bad Example:
<!-- FAIL - No value --> <input type="submit"> <input type="button"> <input type="reset">
Good Example:
<!-- PASS - Descriptive values --> <input type="submit" value="Submit Application"> <input type="button" value="Save Draft"> <input type="reset" value="Reset Form">
Solution 2: Specific Button Types
Use appropriate values for different button types.
Good Examples:
<!-- Submit buttons --> <input type="submit" value="Submit"> <input type="submit" value="Send Message"> <input type="submit" value="Sign Up"> <input type="submit" value="Create Account"> <input type="submit" value="Place Order"> <!-- Button buttons --> <input type="button" value="Add Item"> <input type="button" value="Calculate Total"> <input type="button" value="Show More"> <input type="button" value="Clear Selection"> <!-- Reset buttons --> <input type="reset" value="Reset Form"> <input type="reset" value="Clear All Fields"> <input type="reset" value="Start Over">
Solution 3: Using aria-label
For internationalization or dynamic content, use aria-label.
Good Example:
<!-- aria-label overrides value for screen readers --> <input type="submit" value="→" aria-label="Submit form"> <input type="button" value="..." aria-label="More options"> <!-- Dynamic button --> <input type="button" id="toggle-btn" value="▶" aria-label="Play video"> <script> function togglePlayPause() { const btn = document.getElementById('toggle-btn'); const isPlaying = btn.value === '❚❚'; btn.value = isPlaying ? '▶' : '❚❚'; btn.setAttribute('aria-label', isPlaying ? 'Play video' : 'Pause video'); } </script>
Solution 4: Image Input Buttons
For <input type="image">, use alt attribute.
Bad Example:
<!-- FAIL - No alt text --> <input type="image" src="submit.png">
Good Example:
<!-- PASS - Descriptive alt text --> <input type="image" src="submit.png" alt="Submit form"> <input type="image" src="search-icon.png" alt="Search"> <input type="image" src="delete.png" alt="Delete item">
Solution 5: Context-Specific Labels
Provide context in the button label.
Bad Example:
<!-- FAIL - Too generic --> <form id="login"> <input type="text" name="email"> <input type="submit" value="Submit"> </form> <form id="newsletter"> <input type="email" name="email"> <input type="submit" value="Submit"> </form>
Good Example:
<!-- PASS - Context-specific --> <form id="login"> <input type="text" name="email"> <input type="submit" value="Sign In"> </form> <form id="newsletter"> <input type="email" name="email"> <input type="submit" value="Subscribe to Newsletter"> </form>
Common Mistakes
1. Missing Value Attribute
<!-- FAIL --> <input type="submit"> <input type="button"> <input type="reset">
2. Empty Value
<!-- FAIL --> <input type="submit" value=""> <input type="button" value=" ">
3. Using Placeholder Instead
<!-- FAIL - Placeholder doesn't provide accessible name --> <input type="button" placeholder="Click here"> <!-- PASS --> <input type="button" value="Click here">
4. Generic Labels
<!-- FAIL - Too vague --> <input type="submit" value="Submit"> <input type="button" value="Click"> <input type="button" value="OK"> <!-- PASS - Specific --> <input type="submit" value="Submit Contact Form"> <input type="button" value="Add to Cart"> <input type="button" value="Confirm Purchase">
5. Image Input Without Alt
<!-- FAIL --> <input type="image" src="button.png"> <input type="image" src="submit.gif" alt=""> <!-- PASS --> <input type="image" src="button.png" alt="Submit form">
Examples by Use Case
Form Submission
<!-- Login form --> <form action="/login" method="post"> <label for="username">Username:</label> <input type="text" id="username" name="username"> <label for="password">Password:</label> <input type="password" id="password" name="password"> <input type="submit" value="Sign In"> </form> <!-- Contact form --> <form action="/contact" method="post"> <label for="name">Name:</label> <input type="text" id="name" name="name"> <label for="email">Email:</label> <input type="email" id="email" name="email"> <label for="message">Message:</label> <textarea id="message" name="message"></textarea> <input type="submit" value="Send Message"> <input type="reset" value="Clear Form"> </form>
Action Buttons
<!-- Calculator --> <input type="button" value="Calculate" onclick="calculate()"> <input type="button" value="Clear" onclick="clear()"> <!-- File operations --> <input type="button" value="Upload File" onclick="uploadFile()"> <input type="button" value="Download Report" onclick="download()"> <!-- Data operations --> <input type="button" value="Save Draft" onclick="saveDraft()"> <input type="button" value="Delete" onclick="confirmDelete()">
Search Forms
<!-- Text button --> <form action="/search" method="get"> <label for="search-query">Search:</label> <input type="text" id="search-query" name="q"> <input type="submit" value="Search"> </form> <!-- Image button --> <form action="/search" method="get"> <label for="search-query2">Search:</label> <input type="text" id="search-query2" name="q"> <input type="image" src="/icons/search.png" alt="Search" width="20" height="20"> </form>
E-commerce
<!-- Add to cart --> <form action="/cart/add" method="post"> <input type="hidden" name="product_id" value="123"> <label for="quantity">Quantity:</label> <input type="number" id="quantity" name="quantity" value="1"> <input type="submit" value="Add to Cart"> </form> <!-- Checkout --> <form action="/checkout" method="post"> <!-- Form fields --> <input type="submit" value="Proceed to Checkout"> <input type="button" value="Continue Shopping" onclick="history.back()"> </form>
Multi-step Forms
<!-- Step 1 --> <form id="step1"> <!-- Fields --> <input type="button" value="Next: Shipping Information" onclick="nextStep()"> </form> <!-- Step 2 --> <form id="step2"> <!-- Fields --> <input type="button" value="Back to Personal Information" onclick="prevStep()"> <input type="button" value="Next: Payment" onclick="nextStep()"> </form> <!-- Final step --> <form id="step3"> <!-- Fields --> <input type="button" value="Back to Shipping" onclick="prevStep()"> <input type="submit" value="Complete Order"> </form>
Image Buttons
<!-- Social sharing --> <input type="image" src="/icons/facebook.png" alt="Share on Facebook" onclick="shareOnFacebook()"> <input type="image" src="/icons/twitter.png" alt="Share on Twitter" onclick="shareOnTwitter()"> <!-- Actions --> <input type="image" src="/icons/print.png" alt="Print this page" onclick="window.print()"> <input type="image" src="/icons/pdf.png" alt="Download as PDF" onclick="downloadPDF()">
Comparison: Input vs Button Element
Input Button
<!-- Limited flexibility --> <input type="submit" value="Submit"> <input type="button" value="Click Me">
Button Element (Recommended)
<!-- More flexible, can contain HTML --> <button type="submit"> <i class="icon-save" aria-hidden="true"></i> Submit </button> <button type="button"> <svg aria-hidden="true">...</svg> Click Me </button>
Dynamic Button Text
// Update input button value function updateButtonText(action) { const btn = document.getElementById('action-btn'); if (action === 'save') { btn.value = 'Saving...'; btn.disabled = true; } else if (action === 'saved') { btn.value = 'Saved!'; setTimeout(() => { btn.value = 'Save Changes'; btn.disabled = false; }, 2000); } } // Toggle button function toggleButton() { const btn = document.getElementById('toggle'); const isActive = btn.getAttribute('aria-pressed') === 'true'; btn.value = isActive ? 'Activate' : 'Deactivate'; btn.setAttribute('aria-pressed', !isActive); }
Testing
Manual Testing
- Find all input buttons on the page
- Verify each has a value, aria-label, or alt attribute
- Check that text is descriptive and specific
- Test with keyboard navigation
Screen Reader Testing
NVDA/JAWS: Tab to input button
Expected: "[Button text], button" not just "button"
Press Enter/Space:
Expected: Button activates correctly
NVDA: Insert+Tab (list form fields)
Expected: All buttons listed with descriptive names
Automated Testing
// Check all input buttons have accessible names const inputButtons = document.querySelectorAll( 'input[type="submit"], input[type="button"], input[type="reset"], input[type="image"]' ); inputButtons.forEach(btn => { const value = btn.value?.trim(); const ariaLabel = btn.getAttribute('aria-label')?.trim(); const alt = btn.alt?.trim(); const type = btn.type; const hasAccessibleName = type === 'image' ? (alt || ariaLabel) : (value || ariaLabel); if (!hasAccessibleName) { console.error(`Input button without accessible name:`, btn); } }); // Using axe-core const results = await axe.run(); const violations = results.violations.filter( v => v.id === 'input-button-name' );
Browser DevTools
// Find all input buttons $$('input[type="submit"], input[type="button"], input[type="reset"], input[type="image"]') // Check for missing accessible names Array.from(document.querySelectorAll('input[type="submit"], input[type="button"], input[type="reset"]')) .filter(btn => !btn.value && !btn.getAttribute('aria-label')) .forEach(btn => console.log('Missing name:', btn)); // Check image inputs Array.from(document.querySelectorAll('input[type="image"]')) .filter(btn => !btn.alt && !btn.getAttribute('aria-label')) .forEach(btn => console.log('Missing alt:', btn));
External Resources
Automate Your Accessibility Testing
Our tool automatically checks for this rule and hundreds of other accessibility issues.
Start Your Free Trial