A page's <title> isn't just the text in the browser tab. It's the first words a screen reader announces; the heading on a search-results card; the label that appears in bookmarks and history. Get it wrong and all of those break at once.
1. What is the document-title violation?
axe-core's document-title rule requires every HTML document to have a non-empty <title> in <head>. It's a direct mapping to WCAG 2.1 2.4.2 Page Titled (Level A).
When title is missing, empty, or identical across pages, users can't tell pages apart — especially when juggling tabs.
2. Why it matters
Title affects more surfaces than almost any other element:
- Browser tab: Most users keep 10-20 tabs open. Title is what they see switching among them.
- Screen reader: NVDA, JAWS, VoiceOver all announce title on page load — the very first thing the user learns.
- Search results: Google derives the SERP heading mainly from title. CTR depends on it.
- Bookmarks and history: Bookmark and history entries default to title. A meaningless title makes a page hard to find a week later.
- Social shares: Without an OpenGraph title, share cards fall back to title.
One element, that many surfaces. A bad title sabotages SEO, UX, and accessibility at the same time.
3. When does it fire?
- No title element: No
<title>in<head>at all. Common on programmatically generated pages. - Empty title:
<title></title>or whitespace-only. - Meaningless default:
<title>Document</title>,<title>Untitled</title>, or a CMS-supplied page id. axe-core can't catch text quality, but for accessibility these still count as missing. - Same title on every page: Technically passes the rule but breaks WCAG 2.4.2's requirement that titles distinguish pages.
- SPA without title updates: When a single-page app changes routes without updating title, users don't hear that they've moved to a new page, and most screen readers stumble on the transition.
4. The fixes
4.1 Page-specific, descriptive title
A good title states the page's purpose in one line. The unique part comes first; the site name comes last, usually separated by a pipe.
<title>Site</title>
<title>Untitled</title>
<title>Home</title><title>Accessible Button Names | Keysonar Blog</title>
<title>iPhone 15 Pro 256GB Blue — $1,299 | Keysonar</title>4.2 Title format
A consistent template helps users orient instantly when switching tabs. Recommended: {Page Title} | {Site Name}. Putting the unique part first reads better for screen readers and improves SERP CTR.
- Home: Site Name + short value prop
- Category: Category Name | Site Name
- Detail: Item Name | Category | Site Name
- Error page: 404 — Page Not Found | Site Name
4.3 Next.js metadata API
In Next.js, generating title in each route's generateMetadata is the cleanest path for both dynamic content and multilingual sites.
// app/blog/[slug]/page.tsx
import type { Metadata } from "next";
export async function generateMetadata({
params,
}: {
params: Promise<{ slug: string }>;
}): Promise<Metadata> {
const { slug } = await params;
const post = await getPostBySlug(slug);
return {
title: `${post.title} | Keysonar Blog`,
description: post.description,
};
}// app/layout.tsx
export const metadata: Metadata = {
title: {
template: "%s | Keysonar",
default: "Keysonar — Web accessibility and SEO health",
},
};
// Then a child page only writes the unique part:
// app/blog/[slug]/page.tsx
return { title: post.title }; // → "Accessible Button Names | Keysonar"4.4 Updating title on SPA route changes
For client-side routes (Vue, React Router, Astro's client islands), title has to be updated by hand. Set document.title in a route hook — but after the page renders.
function usePageTitle(title: string) {
useEffect(() => {
document.title = `${title} | Keysonar`;
}, [title]);
}
function ProductPage() {
const product = useProduct();
usePageTitle(product?.name ?? "Loading");
// ...
}5. Title in multilingual sites
When a site has TR and EN versions, translate title too. An English "Home" on a Turkish page mismatches both the screen reader and the search engine's language classification.
export async function generateMetadata(): Promise<Metadata> {
const locale = await getLocale();
const t = await getTranslations(locale);
return {
title: t("blog.metaTitle"),
description: t("blog.metaDesc"),
};
}The title's language must match <html lang="tr">. Mismatches will surface as separate violations under axe-core's html-lang-valid rule.
6. Cases that get missed
- iframes: The document inside an iframe needs its own title; otherwise axe-core's
frame-titlerule fires. - Title length: Google truncates around 580px in SERPs. axe-core doesn't check length, but long titles hurt search appearance. Aim for 50-60 characters.
- Dynamic glyphs: Emoji or unusual characters in title get announced as “star symbol” or skipped entirely by screen readers. Skip them unless they add meaning.
- 404 / error pages: Often forgotten and left as “Untitled.” They need titles too.
- Downloadable PDFs: The PDF's embedded title metadata also needs to be set; screen readers read PDFs the same way.
7. How to test
- Look at the browser tab: Does it tell you what page you're on at a glance?
- axe DevTools / Lighthouse: Both flag missing title immediately.
- Keysonar SEO Tools: Lists every page's title, surfaces missing, too-short, too-long, and duplicate titles. Essential for site-wide consistency.
- Screen-reader page load: With NVDA or VoiceOver running, open a new page and confirm the title is read clearly and accurately.
8. Quick checklist
- Every page has a non-empty <title>.
- Title is page-specific and reflects the content — not the same on every page.
- Format is consistent: {Page Title} | {Site Name}.
- SPA routes update title on navigation.
- Title language matches the page's <html lang> value.
- Title fits in roughly 50-60 characters and won't be truncated in SERPs.
- iframes have their own title.
- 404 and error pages carry meaningful titles.
- Title is not 'Document', 'Untitled', or a CMS default.
9. References
- WCAG 2.1 SC 2.4.2 — Page Titled — Level A
- HTML Living Standard — The title element
- Google Search Central — Title link best practices
- Next.js — Metadata API