Fix

Title attributes that say everything twice

The title attribute promises a helpful tooltip and mostly delivers noise: unreachable by touch or keyboard, and read out by screen readers on top of a name they have already announced. Removing the redundant ones makes every announcement cleaner.

Auto-applied WCAG 4.1.2

Who this locks out

Empty and duplicate title attributes add announcement noise; conflicting ones get a human decision.

  • Screen-reader users, who hear the same control name announced twice
  • Touchscreen and keyboard users, for whom the tooltip never appears at all
  • Anyone relying on consistent names, when a stale title contradicts the visible label

The transform

Before

<a href="/delivery/" title="Delivery information">Delivery information</a>

After

<a href="/delivery/">Delivery information</a>

Detection

How this is tested here

Every scan runs three independent engines and 19 custom checks against your rendered pages, and records which engine flagged what. These are the real rules behind this fix family.

WCAG success criteria this family covers, and the engine rules that detect them. Generated from the engine's own tables — never edited by hand.
Criterion Level Detected by What we do
4.1.2 Name, Role, Value Most affects: People who are blind · People with motor impairments · People with low vision A axe-core (aria-allowed-attr, aria-braille-equivalent, aria-command-name, aria-conditional-attr) · IBM Equal Access (aria_accessiblename_exists, aria_activedescendant_valid, aria_attribute_allowed) · Klarvo probe (klarvo.broken-aria-ref) auto

What Klarvo Access does

Klarvo removes title attributes that are empty or exactly duplicate the accessible name the element already has, server-side in the HTML your store serves. A title that says something different is routed to your review queue instead — a person decides which name wins — and every removal is re-validated to prove the preferred name survived it.

Auto-applied

Generated, then applied server-side in the HTML your store actually serves — and independently re-validated in the served page before it counts as resolved.

An automated finding is evidence, not a certificate. Conformance is certified only by the professional audit — how that works.

Do it yourself

Fixing this by hand

What a redundant title actually does

The title attribute is one of HTML’s oldest promises: hover over an element, and a small tooltip offers a little more. The promise has aged badly. Touchscreens have no hover, so the tooltip never appears for the majority of your traffic. Keyboard focus does not generally trigger it either, so the visitors most dependent on non-mouse input never see it. What remains is what assistive technology does with it — and there, a redundant title is actively worse than useless.

The accessible name of a control is computed by a defined order of precedence — the W3C’s accessible name computation — and for a link with visible text, that text is the name. A title on the same element does not replace the name; it typically becomes the description, which many screen readers announce after the name. So <a href="/delivery/" title="Delivery information">Delivery information</a> is heard, depending on settings, as “Delivery information, link, Delivery information”. One control, one meaning, announced twice. Multiply it by every item in a menu built with titles, on every page of a shop, and the store becomes measurably slower to listen to — a verbosity tax paid only by the people using ears instead of eyes.

That is the duplicate case. The conflicting case is quieter and nastier: a title that says something different from the visible text, usually because one of them was edited years after the other. The visible link says “Delivery information”; the stale title still says “Shipping FAQ 2019”. Now a screen-reader user hears two competing names for one destination, a support conversation runs aground on which name the customer saw, and nobody sighted ever notices, because nobody hovers.

Under WCAG this family belongs to 4.1.2 Name, Role, Value — the criterion that requires controls to expose a coherent programmatic name. A redundant title is rarely a hard failure of it on its own, and we will not pretend otherwise; it is accessible-name hygiene, the difference between markup that technically computes a name and markup whose announcements are clean, single and trustworthy. Hygiene is worth doing precisely because it is cheap: this is one of the few families where the correct fix is deletion.

WordPress accumulates these attributes in layers. The Menus screen has carried a Title Attribute field since the beginning, dutifully filled by hand or by migration plugins with a copy of the label. Content written before WordPress 3.5 had title attributes stamped onto every inserted image, and posts from that era still serve them. Classic themes commonly print title="<?php the_title_attribute(); ?>" on every product and post permalink — one template line, thousands of duplicated announcements. Page builders offer a per-element title box that gets filled “for SEO”, a belief search engines retired long ago.

How it is detected here

No engine in the scan names redundant titles as a discrete rule. The nearest named neighbour is axe’s best-practice rule image-redundant-alt, which catches the sibling pattern of alt text repeating adjacent visible text — the same announce-it-twice injury by a different route — but no engine rule examines the title attribute against the name it shadows. So this family is found by Klarvo’s own deterministic pass over the exact bytes your server sends.

The pass examines every element carrying a title attribute and computes the element’s preferred accessible name conservatively, in the order assistive technology would: an aria-label if one is present; an image’s alt; a single, unambiguous aria-labelledby reference that resolves to exactly one element; otherwise the visible text of links, buttons and summary elements. The reading of visible text is deliberately strict — nested same-tag markup, hidden content or anything that cannot be extracted safely means no claim is made at all.

Each flagged element then lands in exactly one of three buckets, and the bucket is the finding:

  • Emptytitle="" or whitespace, contributing nothing to anyone.
  • Exact duplicate — the title matches the preferred name character for character after trimming.
  • Different — the title and the preferred name disagree, which is a genuine editorial question.

An element whose preferred name cannot be computed is not flagged unless its title is empty: where the machine cannot prove a duplicate, it does not assert one. Every finding is attributed to the exact page whose served HTML contained it.

What Klarvo does about it

This family splits across two tiers, and the split follows the evidence rather than convenience.

Empty titles and exact duplicates are auto tier. Removing an attribute that is blank, or that repeats the element’s name letter for letter, changes nothing about meaning — it deletes an echo. The fix is a server-side removal of the attribute in the HTML your store serves — never a client-side script papering over it — recorded with its reason: an empty title, or a title exactly duplicating the preferred name. The operation is idempotent, and everything else on the element is left untouched.

A title that differs is review tier, always. Two names exist for one control, and automation never guesses which one your customers should hear. The queue shows the element with both strings side by side — the visible or programmatic name, and the title contradicting it. Approve, and the title is removed so the preferred name stands alone. Or keep the title, and take the wording question into your own content — perhaps the visible text is the stale one. Either way a human decided; nothing in this bucket is ever applied on the machine’s own judgement.

Then comes the part that makes the removal trustworthy: re-validation asserts presence, not just absence. The page is fetched fresh, and the served HTML must show the title gone and the preferred accessible name still present on that element before the fix is recorded. A removal that somehow cost the element its name would fail that check rather than pass it quietly — deleting an attribute is easy, and proving the announcement still works is the actual job. An automated finding is evidence, not a certificate, and so is an automated deletion.

How to fix it yourself

Start with the menus, because they multiply. On Appearance → Menus, open Screen Options at the top and tick “Title Attribute” — the field is hidden by default, which is how it stays filled for years unseen. Clear it wherever it repeats the label. A filter in your child theme’s functions.php does the same job structurally, stripping only the redundant ones while leaving any title that carries distinct information for you to judge:

add_filter( 'nav_menu_link_attributes', function ( $atts, $item ) {
	$title = isset( $atts['title'] ) ? trim( $atts['title'] ) : '';
	$label = trim( wp_strip_all_tags( $item->title ) );
	if ( '' === $title || $title === $label ) {
		unset( $atts['title'] );
	}
	return $atts;
}, 10, 2 );

Then the theme templates. Search your child theme for title=" — the classic offenders are permalinks in content.php, archive-product.php overrides and breadcrumb partials printing the_title_attribute() alongside the visible title. Delete the attribute from the template and the duplication disappears from every page at once:

<?php // Before ?>
<a href="<?php the_permalink(); ?>" title="<?php the_title_attribute(); ?>"><?php the_title(); ?></a>

<?php // After ?>
<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>

Audit what the pages actually serve. A console snippet gives you the same three buckets the scan uses, on any page you are looking at:

document.querySelectorAll('[title]').forEach((el) => {
  const title = (el.getAttribute('title') || '').trim();
  const name = (el.getAttribute('aria-label') || el.getAttribute('alt') || el.textContent || '')
    .trim().replace(/\s+/g, ' ');
  const verdict = title === '' ? 'EMPTY' : title === name ? 'DUPLICATE' : 'DIFFERS';
  console.log(verdict, el.tagName, JSON.stringify(title), '→', JSON.stringify(name));
});

Work the DUPLICATE rows mechanically and read every DIFFERS row before touching it — some are stale copy to delete, and some are the only place a real distinction was ever written down, in which case the fix is to move that wording into the visible text or an aria-label, not to lose it.

Know which titles are load-bearing before you sweep. An <iframe title="…"> is the accessible name of the frame — your embedded map, video or payment field depends on it, and stripping it creates a genuine 4.1.2 failure. An <abbr title="…"> expansion is a documented, legitimate use. And a title on a form field is not redundant so much as misplaced — it is often the only label the field has, and the durable fix is a real, visible label rather than a deletion. The automated pass sidesteps all three by design; a hand-rolled search-and-destroy does not, so sweep with the buckets, not with a regex.

WooCommerce spots worth a specific look: product-grid permalinks in older or heavily customised themes, breadcrumb plugins that title every crumb with its own text, social icons whose title repeats an aria-label, and gallery images from ancient content still carrying their upload-era titles.

How you know it worked

  1. Hover a fixed link. No tooltip should appear repeating what the link already says — the small, satisfying proof that the echo is gone.
  2. Listen to it. With VoiceOver (Cmd+F5) or NVDA, tab to the control: one name, announced once — “Delivery information, link” — with nothing trailing behind it.
  3. View the served source. The title attribute is absent from the element and the visible text or label is intact — which is precisely the pair of facts re-validation checks before the fix is recorded, in that order of importance.
  4. Re-scan. The empty and exact-duplicate counts fall to zero as removals are confirmed on fresh fetches. The DIFFERS findings that remain are not a backlog the automation failed to clear — they are the editorial questions it correctly refused to answer, waiting in the queue for the only party qualified to settle them.
  5. Spot-check the survivors: your iframes still carry their titles, your abbreviations still expand, and any title you deliberately kept still says something the visible text does not. A clean result here is not silence — it is one clear name per control, everywhere.

Sources: W3C WAI — Understanding SC 4.1.2: Name, Role, Value · W3C WAI — Technique H33: Supplementing link text with the title attribute (advisory, with documented user agent limitations) · W3C — Accessible Name and Description Computation 1.1 · Deque University — axe rule image-redundant-alt (axe-core 4.12) · MDN — The title global attribute

FAQ

Questions this raises

If the title attribute is harmless to most visitors, why bother removing it?

Because the visitors it is not harmless to hear it on every link, all day. One duplicated announcement is a half-second of noise; a navigation menu of twelve duplicated announcements, met on every page, is a tax on listening. Clearing the redundant titles also drains the swamp: once the exact duplicates are gone, the titles that remain are the ones genuinely saying something different, and those deserve attention rather than camouflage.

Why do some of my titles fix automatically and others wait for review?

The split follows the evidence. A title that is empty, or matches the accessible name character for character, can be removed with nothing lost — the announcement it fed was pure repetition, and re-validation proves the name survived. A title that differs is two candidate names for one control, and choosing between them is a claim about your content. Automation never guesses which name your customers should hear; the queue shows you both and you decide.

Are there title attributes I should keep?

Yes, and the pass is built not to touch the load-bearing ones. An iframe title is not decoration — it is the accessible name of the frame, and removing it creates a failure rather than fixing one. An abbreviation expansion on an abbr element is a documented use. And any element where no preferred name can be computed is left entirely alone: automation that cannot prove a duplicate does not invent one.

Find out whether your store has this one.

The free scan checks your real pages — home, product, populated basket, hydrated checkout — with all three engines and all 19 Klarvo checks, and tells you exactly what it found and where.