x

Main chapters

  1. LimeSurvey Cloud vs LimeSurvey CE
  2. LimeSurvey Cloud - Quick start guide
  3. LimeSurvey CE - Installation
  4. How to design a good survey (Guide)
  5. Getting started
  6. LimeSurvey configuration
  7. Introduction - Surveys
  8. View survey settings
  9. View survey menu
  10. View survey structure
  11. Introduction - Questions
  12. Introduction - Question Groups
  13. Introduction - Surveys - Management
  14. Survey toolbar options
  15. Multilingual survey
  16. Quick start guide - ExpressionScript
  17. Advanced features
  18. General FAQ
  19. Troubleshooting
  20. Workarounds
  21. License
  22. Version change log
  23. Plugins - Advanced
 Actions

Accessibility

From LimeSurvey Manual

This is a developer page. It describes how accessibility is implemented in LimeSurvey 7, which conventions new code must follow, and which gaps are known. It does not state WCAG conformance for any part of LimeSurvey.

Goal

LimeSurvey aims to meet WCAG 2.2 level AA in both the public survey pages and the administration interface. Many customers need this to meet legal requirements such as EN 301 549 or national accessibility laws.

Two rules apply to all work:

  • All question types must stay usable. Do not remove or simplify a question type for everyone; give it an accessible fallback instead (see Survey pages).
  • Accessibility is part of every change, not a separate project. A pull request that adds or changes UI must keep it keyboard operable and correctly labelled.

Where the code lives

Area Location Notes
Admin views application/views/, widgets in application/extensions/ PHP views, Bootstrap 5
Shared admin accessibility scripts assets/packages/adminbasics/src/accessibility/ Loaded on every admin page, see Admin interface
Question type markup application/views/survey/questions/answer/ Twig, shared by all survey themes
Survey themes themes/survey/ fruity_twentythree is the default theme. bootswatch and fruity extend vanilla. fruity_twentythree extends nothing and uses its own JS/CSS bundle built from assets/survey_themes/fruity_twentythree/, so behaviour can differ between themes.
Question themes themes/question/ For example image_select-listradio
React survey editor editor/ Separate build

Conventions

General

  • Use native HTML elements first ( <button>, <label>, <fieldset>, <legend>, <table> with ). Only add ARIA roles when no native element fits.
  • Use real heading elements ( h1–h6 ) in a logical order. Do not use role="heading" with aria-level when a real heading element can be used.
  • Use the Bootstrap 5 class visually-hidden for text that only screen readers should read, and visually-hidden-focusable for skip links. Do not use sr-only; it is a Bootstrap 3/4 class.
  • Every form control needs an accessible name: a <label for>, or aria-labelledby pointing to visible text. Use aria-label only when there is no visible text, for example on icon-only buttons.
  • Hide decorative icons from screen readers with aria-hidden="true". Do not put aria-hidden on anything that can receive focus.
  • Every id referenced by aria-labelledby, aria-describedby or aria-controls must exist in every theme that renders the markup.
  • Translate all text read by screen readers (aria-label, captions, visually hidden text) with gT()/eT(). Use one string with %s and sprintf() instead of concatenating translated parts.
  • Images need an alt attribute. Use alt="" for decorative images.
  • If the user lacks permission for an action, do not show the control. If the action is unavailable for another reason, show it as disabled (disabled attribute, or aria-disabled="true" on elements that cannot take disabled).
  • Changing state with a click must also work with the keyboard: Enter for links, Enter and Space for buttons.
  • Never remove the focus outline without providing a visible replacement.

Admin interface

The following helpers are loaded on every admin page by the adminbasics package or by the related widget. Use them instead of writing new solutions.

Need How
Tabs Mark up tabs with role="tablist", role="tab", aria-selected, aria-controls and role="tabpanel". accessibility/tabsControl.js adds arrow, Home and End key navigation and the roving tabindex for all Bootstrap tabs and pills. After you insert tabs dynamically outside an ajax call, call LS.refreshTabsA11y().
Modals The modal needs aria-labelledby pointing to the id of the modal title, and aria-modal="true". The close button needs aria-label="<?php eT('Close'); ?>". After the modal closes, focus must go back to the element that opened it.
Data grids Pass lsCaption to CLSGridView. It is rendered as a visually hidden <caption>. application/extensions/admin/grid/assets/sortAccessibility.js makes sort links work with the keyboard and keeps focus on the sorted column after the ajax reload.
Status and flash messages Use application/views/layouts/messagebox.php or Yii::app()->user->setFlash(). The message container has role="status", aria-live="polite" and tabindex="-1", and accessibility/statusMessageFocus.js moves focus to it so screen readers announce it after a redirect.
Select2 dropdowns Give the <select> a <label for> or aria-labelledby. select2-aria.js (loaded by WhSelect2) copies the label to the Select2 combobox and search field and keeps aria-expanded in sync.
Button groups ButtonGroupWidget takes ariaLabelledBy (preferred) or ariaLabel.
Tooltips accessibility/dismissTooltipsOnEscapePress.js closes open tooltips with Escape. Never put information only in a tooltip.
Page title The browser title is built from the topbar title ($aData['topbar']['title']) or the title bar title. Set one on every page so screen reader users can tell pages apart.

Survey pages

  • The question text has the id ls-question-text-{SGQ}. Answer containers use role="group" or role="radiogroup" with aria-labelledby pointing to it. Array rows are labelled by the row text, and each radio is labelled by its column header.
  • JavaScript widgets need a fallback that works with screen readers and without JavaScript:
    • ls-js-hidden-sr: visually hidden when JavaScript is on, still read by screen readers. Used for the native <select> fallback of the ranking question.
    • ls-js-hidden / ls-no-js-hidden: hidden when JavaScript is on / off.
    • Good example: the 5-point emoji slider puts aria-hidden on the visual widget and keeps the real radio buttons as visually-hidden inputs.
  • Expression Manager relevance adds the classes ls-irrelevant ls-hidden (display:none) on the relevance:on/relevance:off events in assets/packages/limesurvey/survey.js (copied for Fruity TwentyThree in assets/survey_themes/fruity_twentythree/core/old_template_core_pre.js). Hidden content is therefore removed from the accessibility tree.
  • The page title has the form "page part - survey title". The page part is the group name when one group is shown, or a label such as Welcome or Thank you!. The prefix "Error: " is added when the page shows errors.
  • Survey administrators can set the Fruity TwentyThree theme option Show popups to On page to show validation errors inline instead of in a modal.

React editor

New components in editor/ must follow the same rules. eslint-plugin-jsx-a11y is installed but not yet enabled in editor/.eslintrc.json.

Testing

Do these checks for every UI change:

  1. Keyboard only: Reach and use every control with Tab, Shift+Tab, Enter, Space, the arrow keys and Escape. Focus must always be visible and must never get trapped.
  2. Screen reader: Test with NVDA (Windows, Firefox or Chrome) or VoiceOver (macOS/iOS, Safari). Check that controls are read with their name, role and state, and that dynamic changes (errors, status messages) are announced.
  3. Automated check: Run axe DevTools or WAVE on the page. These find only part of the problems, so they do not replace steps 1 and 2.
  4. Zoom and reflow: At 200 % browser zoom and at 320 px width, nothing may be cut off and there may be no horizontal scrolling (except for data tables).
  5. Contrast: Text needs a contrast ratio of at least 4.5:1, large text and UI components at least 3:1.

Survey authors can check their own question and help texts with the accessibility checker button in the HTML editor (CKEditor a11ychecker plugin).

Known gaps

These problems were found in the code on master in October 2026. Report new problems in the bug tracker and remove items here once they are fixed.

Survey pages

  • The ranking and multiple short text questions reference ls-question-validation-{SGQ} in aria-labelledby, but only Fruity TwentyThree renders an element with that id. In vanilla, bootswatch and fruity the reference points to nothing.
  • Mandatory questions are announced only by text. No input has required or aria-required.
  • Inputs with a validation error do not get aria-invalid. Expression Manager tips change only their CSS class; only Fruity TwentyThree announces the change through a live region.
  • Help text is not connected to the inputs with aria-describedby.
  • Questions that appear or disappear through relevance are not announced.
  • After a page is submitted with errors, focus does not move to the first error. The function focusFirst() in the theme JS exists but its call is commented out.
  • Only Fruity TwentyThree has a skip link; vanilla and the themes extending it have none.
  • The progress bar has aria-hidden="true" in all themes.
  • In list radio questions each option is labelled by the full question text plus the option text, so the question is read again for every option.
  • The error modal close button (application/models/TemplateConfig.php) has aria-hidden="true" and no aria-label.
  • File upload: the upload modal has no aria-labelledby/aria-modal, its close button has no aria-label, and the upload status is not announced.
  • Image select question theme: the image is the only content of the label and has no alt, so the radio buttons have no accessible name.
  • Slider (multiple numeric): the reset button has only an icon and a tooltip, no aria-label.
  • Date picker: the calendar button is a
    and cannot be focused with the keyboard. Typing the date in the text field works.
  • Short text with map: the map search field has no label.
  • Array dual scale (dropdown layout): role="group" is set on <col> elements, where it has no effect.
  • None of the Fruity TwentyThree colour variations is a high contrast variation, and the colour options do not check contrast.

Admin interface

  • BaselineModalWidget does not set aria-labelledby or aria-modal on the modal.

Tooling

  • eslint-plugin-jsx-a11y is not enabled for the React editor.
  • There are no automated accessibility tests in the test suite.

History

This page replaces the planning notes from 2017 for LimeSurvey 2.x/3.x. They are kept in the page history.

References