Skip to content

RESOURCES / BLOG

Building an EAA-Ready, Accessible Media Gallery With Cloudinary and MDN Standards

Why It Matters

  • With the European Accessibility Act (EAA) enforcement date now in June 2025, developers in the EU and those serving EU customers have a legal obligation to make their digital products accessible.
  • Cloudinary’s transformations can be used to improve accessibility through simple URL parameters.
  • Developers can keep their client-side code cleaner, more modular, and easier to maintain.

As the European Accessibility Act (EAA) enforcement has went into effect, developers across the EU and beyond are being called to make websites and digital services more accessible. While compliance is a legal obligation, it is also a meaningful step toward building inclusive, user-centered experiences.

This tutorial walks through how to build an accessible media gallery using Cloudinary for media delivery and enhancement, combined with practical techniques from the MDN Web Accessibility guidelines. You’ll learn how to apply semantic HTML, support assistive technologies, and enhance visual content with captions, contrast, and responsive design.

The European Accessibility Act (EAA) aims to ensure that key digital products and services in the EU are accessible to people with disabilities. It covers areas like public websites, online retail platforms, mobile apps, audio-visual media, and online banking.

The EAA is built on four core accessibility principles that every developer should understand:

  • Perceivability. Content must be presented in ways that are accessible, such as through screen reader compatibility, alt text for non-text content, and high-contrast visuals.
  • Operability. Interfaces should be usable with a keyboard and accessible input methods, without requiring a mouse.
  • Understandability. Users must be able to follow navigation and actions clearly, with consistent layout and helpful feedback.
  • Robustness. Websites should work reliably with a wide range of technologies, including assistive tools like screen readers.

These principles are consistent with the Web Content Accessibility Guidelines (WCAG), and the MDN accessibility docs offer concrete guidance for putting them into practice.

Before we dive into the code, here’s the working example of the accessible media gallery we’re going to build.

It includes:

  • Alt text for images.
  • Keyboard operability.
  • Cloudinary-enhanced media (subtitles, contrast, and color-blind features).
  • Responsive light and dark mode support.

All of this aligns with the core principles of the European Accessibility Act, and follows guidance from both MDN Web Docs and WCAG 2.1.

Note: Best viewed in a modern browser like Chrome or Edge with JavaScript enabled.

To keep our code modular and maintainable, we’ll organize the project into three separate files:

/accessible-gallery

├── index.html    
├── styles.css    
└── script.js     

We’ll walk through each part, starting with the HTML layout.

To build anything accessible, the foundation must be meaningful to both humans and machines. HTML is not just about outputting elements to a screen, it’s about building a structure that browsers, assistive technologies, and users can understand and navigate reliably.

We’ll begin with index.html.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Accessible Media Gallery</title>
  <link rel="stylesheet" href="styles.css" />
</head>

Code language: HTML, XML (xml)

This head section sets the tone for everything that follows.

  • The lang="en" attribute may look small, but its impact is big. It’s the first hint to a screen reader like NVDA or VoiceOver about how to interpret text phonetically. Without it, assistive tools may default to incorrect pronunciation or miss key localization cues. As recommended by the MDN Accessibility docs, this should be present on every page.
  • The meta viewport ensures content scales on all screen sizes, a necessity not just for UX, but also for accessibility, particularly for users with motor disabilities who rely on zoom features on mobile.
  • The stylesheet link (styles.css) may seem purely visual, but the way we use color, contrast, and spacing in that file directly influences the perceivability of our content, one of the four foundational principles of the EAA.
<body>
  <header>
    <h1>Accessible Media Gallery</h1>
    <p>
      This demo follows the
      <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility" target="_blank">MDN Accessibility Guidelines</a>,
      utilizes <a href="https://cloudinary.com/blog/simple_steps_to_make_your_site_accessible_with_cloudinary" target="_blank">Cloudinary's accessible media tools</a>,
      and aligns with the <a href="https://commission.europa.eu/.../european-accessibility-act_en" target="_blank">European Accessibility Act</a>.
    </p>
  </header>

Code language: HTML, XML (xml)

Think of this header as the page’s thesis. It gives users, especially screen reader users who may skim by landmarks a clear and early summary of what this content is and why it exists. MDN recommends using <header> elements to introduce regions, especially when followed by informative content.

This content also links directly to the governing guidelines we’re aligning with: MDN for technical practice, Cloudinary for accessible media delivery, and the EAA for legal and inclusive design frameworks.

<main>
  <section class="gallery" aria-label="Accessible image and video gallery" role="list"> 
Code language: HTML, XML (xml)

Now we’re moving into the heart of the page, a region where the actual interactive content lives. The use of <main> is important. It defines the primary content of the document, letting assistive tech bypass repetitive elements and jump straight to what matters.

Inside <main>, we use a <section> for our media gallery. But more than just HTML, we add:

  • aria-label="Accessible image and video gallery"

This provides a human-friendly description that screen readers will announce — even if the content inside is abstract or media-heavy.

  • role="list"

This gives structure. Each media item inside will act like a list item. Screen readers will announce how many items are inside and the role each item plays, an improvement over default <div> wrappers.


<figure tabindex="0" role="group" aria-label="Balloons image enhanced for color-blind users">
  <img
    src="https://res.cloudinary.com/demo/image/upload/e_assist_colorblind,w_600,q_auto,f_auto/balloons.jpg"
    alt="Colorful balloons floating in the sky with enhanced visibility for color blindness"
    loading="lazy" />
  <figcaption>
    Color-blind assisted view<br />
    <a href="https://cloudinary.com/documentation/effects_and_artistic_enhancements#assist_people_with_color_blind_conditions" target="_blank">
      Cloudinary Docs: e_assist_colorblind
    </a>
  </figcaption>
</figure>

Code language: HTML, XML (xml)

Let’s unpack this from top to bottom, structure, semantics, and transformation and how each piece maps to EAA requirements and MDN best practices.

According to MDN’s HTML structure roles, the <figure> element is ideal for self-contained media. When paired with <figcaption>, it forms a complete accessible media object that:

  • Gives context to the image.
  • Allows screen readers to interpret it as a single unit.
  • Makes content more understandable for all users.

The addition of tabindex="0" turns the figure into an interactive focusable region. This is crucial under the EAA’s operability principle: Users must be able to explore media without relying on a mouse. On many websites, non-interactive <img> tags are invisible to keyboard users. By making the entire figure focusable, we let users pause and explore the context just like they would on an image carousel.

This is where semantic richness meets accessibility. While <figure> and <figcaption> already provide good HTML semantics, role="group" explicitly signals to assistive technology that all children of this element belong to a shared, meaningful cluster.

Meanwhile, aria-label="Balloons image enhanced for color-blind users" gives that group a clear name when it’s read aloud by screen readers. Without this, screen reader users might only hear “image” or “figure”, lacking meaningful context.

Now let’s talk about the real star of this block: the image URL.

https://res.cloudinary.com/demo/image/upload/e_assist_colorblind,w_600,q_auto,f_auto/balloons.jpg
Code language: JavaScript (javascript)

Here’s what’s happening:

  • e_assist_colorblind. This is a Cloudinary effect that adds pattern overlays or hue shifts to make images more readable for users with color vision deficiencies one of the most common forms of visual disability.

This transformation directly supports the EAA’s perceivability principle, which mandates that color not be the only means of conveying information.

  • w_600. Resizes the image to a manageable width improving performance and avoiding zooming issues that can disrupt layout.

  • q_auto,f_auto. Ensures optimal compression and format (like WebP), which helps robustness ensuring the image renders consistently across devices and assistive tech.

Cloudinary Docs: Assist Color Blind

This is more than a media card. It’s a compliance-ready, user-friendly building block that’s:

  • Keyboard navigable.
  • Properly labeled.
  • Color accessible.
  • Semantically grouped.
  • Responsively delivered.

Unlike the previous block, which enhanced the viewing experience for users, this section helps developers test and validate their visual design choices. By applying Cloudinary’s e_simulate_colorblind transformation, we generate a version of the image that reflects how it might appear to someone with color vision deficiency such as red-green color blindness.

<figure tabindex="0" role="group" aria-label="Simulated view of landscape for color-blind users">
  <img
    src="https://res.cloudinary.com/demo/image/upload/e_simulate_colorblind,w_600,q_auto,f_auto/sample.jpg"
    alt="Simulation of mountain landscape for color-blind testing"
    loading="lazy" />
  <figcaption>
    Developer-facing color-blind simulation<br />
    <a href="https://cloudinary.com/documentation/transformation_reference#e_simulate_colorblind" target="_blank">
      Cloudinary Docs: e_simulate_colorblind
    </a>
  </figcaption>
</figure>
Code language: HTML, XML (xml)

This transformation is essential when validating whether your content communicates without relying solely on color. It visually exposes potential accessibility issues, such as indistinguishable buttons or charts, before they reach real users.

Cloudinary handles this simulation entirely through the image URL no external tooling, plugins, or local preprocessing needed. It becomes part of your design process, not an afterthought, making accessibility testing easy to integrate into everyday workflows.

The European Accessibility Act requires developers to ensure content remains understandable to people with visual impairments, including color blindness.

This simulation supports that mandate and mirrors MDN’s guidance on color contrast, which emphasizes not using color alone to communicate meaning.

Cloudinary documentation: e_simulate_colorblind

Some users don’t need high magnification or screen readers, they simply need better contrast. This is especially true for people with low vision or glare sensitivity. An image that looks beautiful to one user may be unreadable to another, especially if it serves as a background or contains overlaid text.

Cloudinary offers a solution: e_brightness_hsb, a transformation that lets you adjust brightness using the HSB (Hue, Saturation, Brightness) color model. It’s applied server-side, directly in the image URL.

<figure tabindex="0" role="group" aria-label="Beach image with improved contrast for text overlay">
  <img
    src="https://res.cloudinary.com/demo/image/upload/e_brightness_hsb:-20,w_600,q_auto,f_auto/beach.jpg"
    alt="Sunset beach scene brightened for better readability"
    loading="lazy" />
  <figcaption>
    Contrast-enhanced image<br />
    <a href="https://cloudinary.com/documentation/transformation_reference#e_brightness_hsb" target="_blank">
      Cloudinary Docs: e_brightness_hsb
    </a>
  </figcaption>
</figure>
Code language: HTML, XML (xml)

In this example, the image has been brightened using e_brightness_hsb:-20, which decreases brightness slightly to improve contrast without washing out the photo. This can make overlaid text easier to read, and can help users who struggle with glare or subtle background variations.

The transformation is also chained with q_auto,f_auto and w_600, which ensures the image is:

  • Automatically optimized in quality and format.
  • Sized for responsive rendering.
  • Delivered efficiently which supports fast loading on assistive technologies and low-bandwidth devices.

This adjustment supports the “perceivable” requirement in the European Accessibility Act, ensuring that content is visible and understandable for all users.

It also follows MDN’s guidance on accessible contrast, which recommends high contrast between foreground and background elements.

Cloudinary documentation: e_brightness_hsb

Video content can be immersive and informative but without subtitles, it excludes a significant portion of users, including those who are deaf, hard of hearing, or navigating in sound-off environments. Adding subtitles isn’t just a UX improvement; it’s a legal requirement under the European Accessibility Act and a best practice outlined by accessibility standards like WCAG.

Cloudinary simplifies this with the l_subtitles transformation, allowing you to embed .srt subtitle files directly into videos via a URL parameter.

<figure tabindex="0" role="group" aria-label="Dog video with English subtitles">
  <video controls aria-label="Dog video with captions" width="100%">
    <source
      src="https://res.cloudinary.com/demo/video/upload/l_subtitles:sample_sub_en.srt/vc_vp9/dog.webm"
      type="video/webm" />
    Your browser does not support the video tag.
  </video>
  <figcaption>
    Accessible video with English subtitles<br />
    <a href="https://cloudinary.com/documentation/transformation_reference#l_subtitles" target="_blank">
      Cloudinary Docs: l_subtitles
    </a>
  </figcaption>
</figure>
Code language: HTML, XML (xml)
  • The l_subtitles:sample_sub_en.srt transformation tells Cloudinary to burn subtitles into the video using a provided subtitle file.
  • These subtitles are visible by default, ensuring they’re available regardless of player UI or device support.
  • The <video> element is labeled with aria-label="Dog video with captions" to make it screen reader friendly.

This method guarantees subtitle visibility especially important when player controls may not be accessible via keyboard or assistive tech.

The European Accessibility Act requires synchronized text alternatives for all multimedia content. This approach with Cloudinary fulfills that requirement and ensures that content remains inclusive.

It also aligns with MDN’s multimedia accessibility guide, which emphasizes the need for captions, transcripts, and audio alternatives in video content.

Cloudinary documentation: l_subtitles

To support users who prefer darker themes whether for comfort, clarity, or accessibility we provide a light/dark mode toggle. While text and backgrounds adapt easily, images can remain too bright or visually out of place.

To help images match the dark environment, we apply Cloudinary’s e_negate transformation, which generates a color-inverted (negative) version of the original image.

This effect isn’t always ideal for photos, but it can improve visibility for icons, diagrams, or light-on-light visuals that otherwise clash with dark backgrounds.

<button id="darkModeToggle" aria-pressed="false" aria-label="Toggle light and dark mode">
  Toggle Light/Dark Mode
</button>
Code language: HTML, XML (xml)
const toggleBtn = document.getElementById('darkModeToggle');

toggleBtn.addEventListener('click', () => {
  const isDark = document.body.classList.toggle('dark');
  toggleBtn.setAttribute('aria-pressed', isDark);

  const images = document.querySelectorAll('img');
  images.forEach(img => {
    const hasNegate = img.src.includes('/e_negate/');
    const cleanSrc = img.src.replace('/e_negate', '');

    if (isDark && !hasNegate) {
      img.src = cleanSrc.replace('/upload/', '/upload/e_negate/');
    } else if (!isDark && hasNegate) {
      img.src = cleanSrc.replace('/e_negate/', '/');
    }
  });
});
Code language: JavaScript (javascript)

The European Accessibility Act encourages adaptable interfaces that meet a range of visual needs.

This technique supports perceivability for users who require dark mode and better contrast.

Cloudinary documentation: e_negate

This overview outlines how each feature in the accessible media gallery aligns with the four core principles of the European Accessibility Act (EAA): Perceivable, Operable, Understandable, and Robust. The features draw on Cloudinary’s media transformation capabilities and MDN’s accessibility best practices to create an inclusive, user-friendly media experience.

  • Visual accessibility (perceivable):
    Features that enhance the visibility and comprehension of media content:

    • e_assist_colorblind. Improves clarity for users with color vision deficiencies.

    • e_brightness_hsb. Adjusts image brightness for better contrast and legibility.

    • e_negate with dark mode toggle. Ensures visual comfort in low-light environments.

    • l_subtitles. Adds subtitles to videos, aiding users with hearing impairments.

    • e_simulate_colorblind. Lets developers preview content as seen by color-blind users.

  • Structure and semantics (perceivable, robust):
    Features that provide meaningful markup for assistive technologies:

    • Semantic HTML (figure, alt). Delivers proper structure and descriptions for screen readers.
  • Interactivity and control (operable, understandable):
    Features that improve navigation and usability for all users:

    • ARIA attributes (aria-label, aria-pressed). Communicate element roles an d states clearly.
    • tabindex="0" on media blocks. Enables keyboard access to interactive content.

Building accessible interfaces isn’t just checking boxes. You should create experiences that respect the needs of all users. By combining Cloudinary’s media transformation features with semantic HTML, ARIA roles, and best practices from MDN, we’ve built a media gallery that is both engaging and inclusive.

This project shows that aligning with the European Accessibility Act doesn’t require complex tools, just a thoughtful approach to markup, media, and interaction.

Whether you’re building a product gallery, educational resource, or content-rich website, accessible media delivery should be a core part of your workflow. Not an afterthought.

Start small, design with empathy, and test often. Accessibility is a shared responsibility and it’s good design.

Start Using Cloudinary

Sign up for our free plan and start creating stunning visual experiences in minutes.

Sign Up for Free
OSZAR »