Directus is an open-source data platform that wraps any SQL database in a REST and GraphQL API and a no-code admin App, so it is used both as a headless CMS for websites and as a back office for internal tools and customer-facing apps. That dual role gives Directus two distinct accessibility surfaces that most CMS platforms do not have to think about together. The first is the familiar headless concern: Directus serves structured content as JSON, and your front-end (Nuxt, Next.js, Astro, SvelteKit, or a mobile app) decides how that data becomes HTML, so accessibility depends on both the collection/field design and the rendering code. The second is the Directus App itself - the admin interface where content editors, who may be blind, low-vision, or keyboard-only, do their daily work; if the collections are modeled with required alt-text fields and the interfaces are configured thoughtfully, those editors can produce accessible content, and if they are not, the editors are blocked before a single page is published. Because Directus stores content in your own database with full schema control, you have unusual freedom to enforce accessibility at the data layer - making an alt-text field required, validating link text, requiring captions on a video relation - which is the most reliable place to do it. With the European Accessibility Act now enforceable for businesses serving EU consumers, and ADA and Section 508 obligations applying to many organizations that run Directus behind public sites and portals, both the content model and the consuming front-end are in scope. This checklist covers the issues we see most often on Directus projects and the specific schema settings, interface choices, and rendering targets that fix each one. None of this is legal advice; consult a qualified attorney for your jurisdiction.

Common Accessibility Issues

critical

File and Image Relations Without a Required Alt-Text Field

WCAG 1.1.1

Directus stores images in the Files collection, which has a 'description' (title/description) field but no required alt text, and content collections reference files through a relation. Editors attach images without supplying alternative text, and front-ends fall back to the filename or omit alt entirely, so informative images reach screen reader users as nothing or as '_DSC0421.jpg'.

How to fix:

Add a dedicated, required alt-text field to the relation between your content collection and its image (or repurpose and require the file 'description' field where context allows), so the model itself collects alt text at content-entry time. Add a Boolean 'decorative' field so editors can mark genuinely decorative images. In the front-end, read alt from that field and render alt="" for decorative images, never falling back to the filename.

Before
// Front-end falls back to filename
<img src={`${API}/assets/${item.image}`} alt={item.image_filename} />
After
// Read the required alt field on the relation
<img
  src={`${API}/assets/${item.image.id}`}
  alt={item.image_decorative ? '' : item.image_alt}
  {...(item.image_decorative ? { role: 'presentation' } : {})}
/>
critical

WYSIWYG and Markdown Output Rendered Without Semantic Guarantees

WCAG 1.3.1

Directus offers a WYSIWYG interface (TinyMCE-based) and a Markdown interface for long-form fields. Editors can apply heading levels by visual size, paste in styled HTML, and create lists that render as plain paragraphs, while front-ends sometimes inject the stored HTML/Markdown with no sanitization or heading-level control. The result is broken outlines, non-semantic lists, and unpredictable structure for screen reader users.

How to fix:

Constrain the WYSIWYG toolbar so editors can only apply heading levels that make sense for where the field renders (avoid offering h1 in body fields). For Markdown, render with a parser that emits semantic HTML and sanitize the output. Set a heading-offset in rendering if the field sits inside a section, so an editor's 'Heading 1' becomes the correct level on the page. Validate that lists, headings, and links survive rendering as real ul/ol, h2-h4, and a elements.

Before
// Markdown injected with no structure control
<div dangerouslySetInnerHTML={{ __html: marked(item.body) }} />
After
// Sanitize and keep semantic elements; offset headings if nested
import DOMPurify from 'dompurify';
const html = DOMPurify.sanitize(marked(item.body));
<div className="rich-text" dangerouslySetInnerHTML={{ __html: html }} />
serious

The Directus App Blocks Editors With Disabilities From Creating Content

WCAG 2.1.1

Accessibility is not only about the public site - the people authoring content matter too. Custom interfaces, layouts, and extensions added to the Directus App can introduce keyboard traps, unlabelled controls, and drag-and-drop that has no keyboard alternative, so a blind or keyboard-only editor cannot reorder items, fill a custom field, or complete a workflow. An author who cannot use the tool cannot produce accessible content.

How to fix:

Prefer Directus's built-in interfaces, which are more consistently accessible, over custom ones, and when you must build a custom extension, test it with a keyboard and a screen reader before deploying it to the App. Ensure every drag-and-drop reorder has a keyboard-operable alternative (move up/down buttons or numeric sort), every custom control has a label, and focus is managed in modals. Treat the authoring experience as in scope for accessibility, not just the front-end.

Before
<!-- Custom interface: drag-only reorder, no keyboard path -->
<div class="draggable" draggable="true">Item</div>
After
<!-- Provide a keyboard-operable alternative -->
<li>
  Item
  <button aria-label="Move item up">Up</button>
  <button aria-label="Move item down">Down</button>
</li>
serious

Link/URL Fields With No Validation Producing Generic Link Text

WCAG 2.4.4

Collections often pair a URL field with a label string field for CTAs and navigation, with no validation on the label. Editors enter 'Click here', 'Read more', or leave it blank, so front-ends render links that fail WCAG 2.4.4 Link Purpose when read out of context, or empty focusable links with no accessible name.

How to fix:

Use Directus field validation (a regex or required rule) to reject empty labels and common generic phrases, and make the label field required. Render link text from the validated label. Where a link is icon-only, require a separate accessible-name field and render it as aria-label. Review navigation and CTA collections for duplicated generic phrases.

Before
<a href={link.url}>{link.label || 'Read more'}</a>
After
// label is required + validated against generic phrases in Directus
<a href={link.url}>{link.label}</a>
serious

Video Relations Without Captions or Transcript Fields

WCAG 1.2.2

Directus stores video as files or external URLs, and the default schema has no place for captions, transcripts, or audio description. Collections that reference video therefore ship players without captions, failing WCAG 1.2.2 Captions (Prerecorded) and excluding Deaf and hard-of-hearing users.

How to fix:

Extend the schema: add a captions file relation (VTT/SRT) and a transcript long-text field to any collection that references video, and make at least one required for published video. In the front-end, attach the captions via a track element and render the transcript near the player. For externally hosted video, require captions on the source platform before the item can be published.

Before
<video src={`${API}/assets/${item.video}`} controls></video>
After
<video src={`${API}/assets/${item.video}`} controls>
  <track kind="captions" src={`${API}/assets/${item.captions}`} srclang="en" label="English" default />
</video>
<details><summary>Transcript</summary>{item.transcript}</details>
serious

Multilingual (Translations) Content Rendered Without a Matching Lang Attribute

WCAG 3.1.1

Directus models translations with a dedicated translations interface and related collection keyed by language code. Front-ends fetch the right translation but frequently forget to set the html element's lang attribute to the language being shown, so screen readers read translated content with the wrong speech engine, failing WCAG 3.1.1.

How to fix:

Use the language code stored on the translation (for example 'de-DE' or 'fr-FR') to set the lang attribute on the html element for the rendered page, and wrap any inline foreign-language passage in an element with its own lang attribute. Drive this from the translation record you fetched, not from browser detection.

Before
<html lang="en"> <!-- always English regardless of translation -->
After
// Derive lang from the Directus translation language code
<html lang={translation.languages_code.split('-')[0] /* 'de' */}>
moderate

Status and Validation Messages Not Announced to Assistive Technology

WCAG 4.1.3

Apps built on Directus often surface save confirmations, filter results, and form validation as visual-only toasts or inline text that is not in a live region, so screen reader users do not hear that an action succeeded, that results updated, or that a field failed validation. This fails WCAG 4.1.3 Status Messages.

How to fix:

In the front-end, place dynamic status text in an appropriate ARIA live region (role="status" or aria-live="polite" for confirmations and result counts, role="alert" for errors) so it is announced without moving focus. Associate field-level validation messages with their inputs via aria-describedby and set aria-invalid on failed fields. Test the announcements with a screen reader.

Before
<div class="toast">Saved</div> <!-- not announced -->
After
<div role="status" aria-live="polite">Changes saved</div>
<span id="email-err" role="alert">Enter a valid email</span>
<input aria-invalid="true" aria-describedby="email-err" />

Directus-Specific Tips

  • Enforce accessibility at the data layer, which is Directus's biggest advantage: make alt-text and caption/transcript fields required in the schema and validate link labels, so editors cannot publish content that is missing them.
  • Treat the Directus App as in scope. The editors using it may be blind, low-vision, or keyboard-only - prefer built-in interfaces, give every drag-and-drop a keyboard alternative, and test custom extensions with a keyboard and screen reader.
  • Constrain the WYSIWYG toolbar and Markdown rendering so headings stay semantic and correctly leveled for where the field appears, and sanitize any stored HTML before injecting it.
  • Add captions and transcript fields to any collection that references video, and make at least one required for published items so a video can never ship uncaptioned.
  • Drive the html lang attribute from the translation language code you fetched, and wrap inline foreign-language passages with their own lang attribute.
  • Put save confirmations, result counts, and validation errors in ARIA live regions in your front-end and admin extensions so screen reader users hear status changes.

axe DevTools

Browser extension and CI tool for automated WCAG testing of both the Directus-driven front-end and custom App extensions. Catches missing alt-text propagation, broken heading structure from WYSIWYG output, and unannounced status messages.

DOMPurify

A sanitizer for HTML produced by the Directus WYSIWYG or Markdown interfaces. Removes unsafe markup while preserving the semantic elements (headings, lists, links) that assistive technology relies on, before you inject content into the page.

WebAIM Contrast Checker

Check the colors used in your front-end theme and any color values stored in Directus fields against the 4.5:1 and 3:1 minimums, including text over background images served from the Files collection.

NVDA + Firefox / VoiceOver + Safari

Manual screen-reader testing verifies both the published front-end and the Directus authoring App - confirming editors can create content and that headings, labels, and status messages are announced correctly.

Keyboard-only testing

Tab through both the front-end and the Directus App, including any custom interfaces and drag-and-drop reordering. If a reorder or custom field cannot be operated without a mouse, neither your editors nor your visitors with disabilities can use it.

Directus Accessibility At a Glance

Plugin / Tool AreaCommon FailureWCAGBest Fix
Image Relations Files collection No required alt; filename fallback1.1.1Required alt field on the relation
WYSIWYG / Markdown long-form fields Broken outline, non-semantic output1.3.1Constrain toolbar, sanitize, offset headings
Directus App authoring Drag-only reorder, unlabelled custom fields2.1.1Keyboard alternatives; test extensions
Translations multilingual lang not set to translation code3.1.1Set html lang from language code
Status Messages save / validation Visual-only toasts, silent errors4.1.3ARIA live regions + aria-describedby

Frequently Asked Questions

Why does accessibility on Directus include the admin App, not just the website?

Because Directus is both a headless CMS and a no-code back office, the people using the admin App every day are part of the accessibility picture. Content editors, moderators, and operators may be blind, low-vision, or keyboard-only, and if a custom interface has a keyboard trap, a drag-and-drop reorder with no keyboard alternative, or unlabelled controls, those team members are blocked from doing their jobs - and blocked from producing accessible public content. So Directus accessibility has two fronts: the public front-end that consumes the API (covered by the usual headless concerns of alt text, semantics, and rendering) and the authoring experience inside the App. Prefer the built-in interfaces, which are more consistently accessible, and test any custom extension with a keyboard and a screen reader before you deploy it.

What is the best place to enforce accessibility on a Directus project?

The data layer, which is where Directus gives you the most control. Because you own the schema, you can make an alt-text field required on the relation between content and images, require a captions or transcript field before a video item can be published, and validate link-label fields against empty values and generic phrases like 'Click here'. Enforcing these in the model means editors physically cannot publish content that is missing them, which is far more reliable than a style guide or a final-step audit. Pair that with a semantic front-end (sanitized, correctly leveled WYSIWYG/Markdown output; alt rendered from the required field; the html lang attribute set from the translation code) and an automated axe check in CI, and you cover both the model and the rendering.

Does Directus being open source and self-hosted affect EAA compliance?

It affects who is responsible, not whether the rules apply. The European Accessibility Act applies to businesses providing covered products and services to consumers in the EU and points to WCAG (via EN 301 549) as the technical benchmark, generally WCAG 2.1 Level AA - regardless of which CMS or hosting model you use. Because Directus is self-hosted and schema-controlled, you have both full responsibility for and full ability to meet that bar: required alt text and captions in the model, semantic rendering in the front-end, the correct lang attribute per translation, accessible status messages, and an authoring App your editors can actually use. Treat these as build requirements verified before launch and keep evidence of your testing. This is general information, not legal advice - consult a qualified attorney for your situation.

Further Reading

Other CMS Checklists