Rails form_with accessibility attributes and landmark role need version-specific diagnosis
0 reputation · 17 Nov 2022, 16:24 UTC
0 reputation · 17 Nov 2022, 16:24 UTC
When building accessible forms with Rails form_with, the helper generates semantic HTML5 markup and automatically applies ARIA attributes such as aria-required and aria-invalid when model validations are present. However, differences in default output across supported Rails versions create uncertainty for teams maintaining applications that span multiple releases.
In Rails 6, form_with omits the role="form" attribute, while Rails 7 includes it by default. This inconsistency can affect how assistive technologies expose the form as a landmark region. Validation errors rendered through Rails error helpers may also be wrapped in a div with the class field_with_errors, which screen readers may not announce without additional ARIA labeling.
Custom form templates or overridden helpers can further alter the default accessibility markup, making the final output difficult to predict without inspecting each form individually. Because these behaviors depend on both the Rails version and any custom view layer, a consistent accessibility strategy is not immediately clear.
Should accessibility attributes be added manually for Rails 6 forms to match Rails 7 defaults, or should custom form builders normalize output across versions? How can validation errors be reliably associated with their inputs for screen-reader announcement without relying on default helper markup? What verification approach confirms that form landmarks and error states remain accessible when templates override standard form_with output?
34775 reputation · 18 Nov 2022, 04:20 UTC
Do not manually add role="form" to Rails 6 forms to match Rails 7. The HTML5 specification states that a <form> with an accessible name (via aria-label, aria-labelledby, or title) is a native form landmark; adding role="form" redundantly can cause duplicate announcements in some screen readers. Instead, adopt a custom FormBuilder that normalizes landmark semantics, required/invalid attributes, and error associations across Rails 6 and 7.
form_with emits no role="form". The <form> becomes a landmark only when it has an accessible name.form_with adds role="form" by default. This is harmless but unnecessary on a named form.aria-required or aria-invalid based on model validations. It only adds the boolean required attribute when a presence validator exists.field_error_proc wraps the input in <div class="field_with_errors">. This wrapper has no ARIA semantics; screen readers will not announce it unless the error message is linked via aria-describedby.Create ApplicationFormBuilder < ActionView::Helpers::FormBuilder and configure it as the default in config/application.rb:
# config/application.rb
config.action_view.default_form_builder = "ApplicationFormBuilder"
Override the methods that affect accessibility:
form_with / form_for – ensure the <form> receives an accessible name (aria-label or aria-labelledby) and omit role="form".text_field, email_field, etc.) – add aria-required="true" when the model attribute has a presence validator; add aria-invalid="true" and aria-describedby="#{field_name}_error" when object.errors[field_name].any?.field_error_proc, render an error <span id="#{field_name}_error" class="error-message" aria-live="polite"> containing the error text, and inject the aria-describedby reference on the input.This single builder insulates the application from framework defaults (including future Rails 7.1+ changes) and guarantees identical markup across versions.
For every invalid field, the generated HTML must satisfy:
aria-invalid="true" and aria-describedby="field_error_id".id="field_error_id", role="alert" or aria-live="polite", and visible error text.If you use Turbo Drive/Frames, ensure the error region persists across visits (e.g., render errors inside a Turbo Frame that is not replaced, or use a global aria-live region).
| Check | Tool / Method | Pass Criteria |
|---|---|---|
| Form landmark | Chrome DevTools → Accessibility pane / Firefox Accessibility Inspector | <form> exposes as "form" landmark with non-empty accessible name |
| Required fields | axe-core (CI) + manual inspection | Each required input has required or aria-required="true" (not both) |
| Invalid fields | axe-core + manual inspection | Each invalid input has aria-invalid="true" and aria-describedby pointing to a live error element |
| Screen-reader announcement | NVDA (Windows) / VoiceOver (macOS) | Form landmark announced; submitting empty required fields speaks errors and moves focus |
| Regression guard | Custom test helper instantiating ApplicationFormBuilder | Generated markup matches expected attribute set for both Rails 6 and 7 test matrices |
Does your application use Turbo Drive or Turbo Frames for form submissions? If yes, the error aria-live region must survive Turbo navigation (e.g., place it outside the replaced frame or use a persistent global live region). This changes the error-rendering strategy in the custom FormBuilder.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.