# Iframes with a missing or generic title

Source: https://easeweb.dev/learn/frame-titles
Topics: Page structure > Embedded frames and third-party widgets have a title (WCAG 4.1.2)
The fix: Give every visible iframe a title attribute that says what it holds, such as “Map of our studio”, in the language of the page, and replace generic titles that come with embed code. For a frame a third-party script inserts, set the title through the widget's own option, or after the frame is inserted, and check the rendered page.
Test with: screen reader
References: https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html https://www.w3.org/WAI/WCAG22/Techniques/html/H64 https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/iframe https://dequeuniversity.com/rules/axe/4.10/frame-title https://dequeuniversity.com/rules/axe/4.10/frame-title-unique https://www.w3.org/TR/WCAG22/#conformance-partial

This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.

## What happens

A pottery studio's "Visit us" page has two embeds. Under "Find us" is a map, pasted in from a map provider's share menu. Under "Take the tour" is a two-minute video, pasted from a video host. A screen reader user who wants directions moves through the page's frames, with the frame key or the screen reader's list of frames, and hears "frame" and then "Video player, frame". Depending on the screen reader, the first may instead read the long address the map loads from. Neither says which one is the map, so they enter each to find out, or give up and look for the address elsewhere.

## Why it fails

An `iframe` puts another document inside the page, and assistive technology announces it as a frame with a name. 4.1.2 needs every user interface component to have a name that can be determined in code, and a frame is one. The map's `iframe` has no `title`, so it has no name at all. The video's frame has `title="Video player"`, which the host's embed code supplies on every video it serves. That is a name, but it describes the player, not the video, and WCAG technique H64, which names frames with `title`, checks that the title "describes the iframe's content". A second video on the page would get the same name, and the two could not be told apart.

## Who is affected

Sighted visitors see a map and a video and notice nothing wrong. The failure is only in the names the code gives the two frames.

## What automation and AI miss

axe's `frame-title` and ANDI flag the map, because its frame has no name. Both accept "Video player", because any non-empty title counts, and axe's `frame-title-unique` only catches two frames sharing one title. Scanners also check the page as it first loads, so a chat or booking widget that inserts its frame after a cookie choice or a click is often never tested. When asked to fix the warning, an AI assistant tends to write `title="iframe"`, `title="Embedded content"` or the provider's name, which says nothing about the content, or adds `aria-hidden="true"` to the frame. That silences the scanner and hides the map from screen readers, while its links stay in the Tab order, announced as nothing. Whether a name says what the frame holds is something a person has to judge.

## Before: a map with no title, and a video titled by its host

```html
<h2>Find us</h2>
<p>4 Mill Lane. Open Tuesday to Sunday, 10:00 to 18:00.</p>
<iframe src="https://maps.example.com/embed?pb=!1m18!1m12…"
  width="600" height="320" style="border:0" loading="lazy"></iframe>

<h2>Take the tour</h2>
<iframe src="https://video.example.com/embed/x7Kq2"
  width="560" height="315" title="Video player"
  allowfullscreen></iframe>
```

The map's frame has no name, and the video's name is the host's default, so neither tells a screen reader user what the frame holds.

## After: a title that says what each frame holds

```html
<h2>Find us</h2>
<p>4 Mill Lane. Open Tuesday to Sunday, 10:00 to 18:00.</p>
<iframe src="https://maps.example.com/embed?pb=!1m18!1m12…"
  title="Map of Kiln Room, 4 Mill Lane"
  width="600" height="320" style="border:0" loading="lazy"></iframe>

<h2>Take the tour</h2>
<iframe src="https://video.example.com/embed/x7Kq2"
  title="Studio tour video"
  width="560" height="315" allowfullscreen></iframe>
```

Each frame now has a short name that says what is in it, so the screen reader reads "Map of Kiln Room, 4 Mill Lane, frame" and "Studio tour video, frame", and someone who wants directions knows which one to enter. The video's default title is replaced, not kept beside the new one. `title` is the HTML attribute for naming a frame, and browsers and screen readers have read it on frames for longer than any ARIA attribute.

## Frames from a third-party widget, plugin or SDK

A chat, booking, payment or review widget usually inserts its iframe with a script, so there is no markup of yours to add the title to. The iframe element is still part of your page, even though what loads inside it comes from the vendor's domain, so it can still be named. Take the first of these that works.

Use the widget's own setting. Many SDKs accept a title, a label or an accessibility option when they are set up, or copy the title from the container you give them. The option's name varies, so check the vendor's documentation. A setting is the only fix that survives the vendor's updates. A CMS or shop plugin that prints the iframe in its template is the same case: look for a setting, then for a template override or filter hook the platform offers. Either outlasts a plugin update, which editing the plugin's own files does not.

If there is no setting, set the title from your own script: in the SDK's ready or load callback if it has one, or by watching the widget's container. Many widgets replace their frame when they open, close or move to the next step, so keep naming it rather than doing it once:

```js
const booking = document.getElementById('booking');
const nameFrames = () => {
  for (const frame of booking.querySelectorAll('iframe')) {
    if (frame.title !== 'Booking calendar') frame.title = 'Booking calendar';
  }
};
nameFrames();
new MutationObserver(nameFrames).observe(booking, { childList: true, subtree: true });
```

Watch the widget's own container, not the whole document. The observer reacts to frames being added, not to attributes, so setting the title does not set it off again. If the widget adds more than one frame, pick out the one people use rather than giving them all the same name. This lasts only while the vendor's markup stays as it is, and does nothing if your script fails to load, so treat it as a stopgap: report the missing name to the vendor, and check the page again after each of its updates.

SDKs often add small helper frames as well, for messaging or fraud checks, that show nothing. One hidden with `display: none` needs nothing. One left in the page at a pixel or two still reaches screen readers, so name it for what it does, such as "Payment security check", and ask the vendor to hide it. A frame inside a closed shadow root can't be reached by any script on your page, and only the vendor can fix it.

When a widget can't be fixed, the failure is still on your page. WCAG allows a statement of partial conformance for third-party content you don't control, but that explains the gap rather than closing it. Name the widget in your accessibility statement, give another way to do the task, such as an email address for bookings, and weigh it when you choose or renew the vendor.

## Implementation decisions

`aria-label` or `aria-labelledby` on an `iframe` also gives it a name, and either overrides `title`. Neither adds anything here, and a frame with both a `title` and an `aria-label` has two names that can drift apart, so set `title` alone.

Where the frame's markup comes from decides where its title is set:

- Frames in your own templates: set `title` in the markup, and make an embed component require it as a prop rather than default to "Embedded content".
- Frames content editors paste: give the embed block a required title field, and check the published page, because some editors strip attributes from pasted code or keep the host's default.
- Frames a third-party widget, plugin or SDK inserts: there is no markup of yours to edit, so use the widget's setting, or set the title from a script, as described above.

Write the name in the page's language. Hosts often supply an English default, which a screen reader set to Japanese reads with the wrong pronunciation. Keep the name short and specific: "Studio tour video", not "Video of a two-minute tour of our pottery studio filmed in spring". The frame's role is announced with it, so the name needs neither "frame" nor "iframe".

A frame that is hidden with `display: none`, `visibility: hidden` or the `hidden` attribute, such as a tracking frame, is not exposed and needs no name. Don't hide a visible frame with `aria-hidden` to quiet a warning: anything focusable inside it still takes focus, with nothing announced.

When the framed document is your own, give it a `<title>` as well, matching the frame's name. Some screen readers read that document title on entering the frame, and it is the page's title if someone opens the frame on its own.

## Verify the fix

1. In the browser's accessibility tree, find every frame on the rendered page, and check that each has a name, that no name is generic, a role or a URL, that no two names are the same unless the frames load the same content, and that no visible frame is hidden with `aria-hidden`.
2. With a screen reader, move from frame to frame (in NVDA and JAWS the frame key is M; JAWS also lists frames with Insert + F9) and check that each name says what the frame holds, without entering it. Then enter each frame and confirm it holds what its name says.
3. Accept cookies, open the chat and switch on every other widget the page has, then check the rendered DOM again for frames inserted after load, and confirm each has a name. Open, close and step through each widget, and check its frame keeps its name when the widget replaces it. Repeat after a widget or embed provider updates its code.

Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat whenever an embed is added or a third-party script changes.

## Limits

This is a starting pattern, not a guarantee for every application. The addresses, IDs and names in the snippets are placeholders. Screen readers announce frames differently: some read the frame's name on reaching it, some read the framed document's title instead, and some read neither until you enter, so listen for the names rather than the exact phrasing. This guide covers naming frames, not whether what is inside them is accessible, such as a map's keyboard controls or a video's captions. Test the complete journey with your target assistive technology.
