Documents must have <title> element to aid in navigation

The page title is the first thing a screen reader announces and the text shown in browser tabs and bookmarks. Without it, blind customers and anyone with several tabs open cannot tell whether they are on the product page, the cart or the checkout.

Rule id
document-title
Source
axe-core 4.13.0
Impact
Serious
Fix tier
AUTO
on Shopify, Shopware, WooCommerce and Magento
Status
runs

What the check does

Ensure each HTML document contains a non-empty <title> element

The rule tests only the top-level document in which axe-core was started; the titles of documents inside an iframe are not tested.

The check reads document.title, the title as the browser reports it at that moment, not the raw HTML. Validiscan’s scanner starts axe-core after waiting, up to a time limit, for the page to load and its network to go quiet, so a title that a script sets before then is the one tested. The text is trimmed, with non-breaking spaces counted as spaces; if anything is left, the page passes. A missing <title> and one holding only whitespace both fail. The evaluation returns a plain true or false, so there is no needs-review outcome.

Nothing about the wording is measured, and titles are not compared between pages. The same “Home” title on every template passes, as does the shop name alone on a product page. Whether a title describes its page and sets it apart from the others is for a person to decide.

How to fix it

Give every page a unique, descriptive <title>, set once in the theme layout template in the form "Page name – Shop name" (for example "Wool Scarf – Example Shop"). Make sure the search, account and error templates output a title too.

WCAG success criteria

EN 301 549 clauses

The EN 301 549 clause of each criterion, by version of the standard
WCAG criterion Version 3.2.1 Version 4.1.1
2.4.2 9.2.4.2 9.2.4.2

Where a recipe fixes it

Shopify: recipe document-title-template

Show the before and after (1)
Before and after Example data

layout/theme.focused.liquid Changes to layout/theme.focused.liquid: 1 added, 1 removed

Changes to layout/theme.focused.liquid: 1 added, 1 removed
Line before Line after Change Code
11 11 <meta charset="utf-8">
12 12 <meta http-equiv="X-UA-Compatible" content="IE=edge">
13 13 <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
14 Removed <title></title>
14 Added <title>{{ page_title }}{% unless page_title contains shop.name %} &ndash; {{ shop.name }}{% endunless %}</title>
15 15 <link rel="canonical" href="{{ canonical_url }}">
16 16 <!-- Theme assets: //cdn.shopify.com/s/files/1/0712/3456/7890/t/12/assets/ (served locally by the fixture server) -->
17 17 {% render 'meta-tags' %}

Shopware: recipe document-title-template

Show the before and after (1)
gives the layout title a shop-name fallback and the empty CMS title is gone Example data

Resources/views/storefront/layout/meta.html.twig Changes to Resources/views/storefront/layout/meta.html.twig: 1 added, 1 removed

Changes to Resources/views/storefront/layout/meta.html.twig: 1 added, 1 removed
Line before Line after Change Code
36 36 {% endblock %}
37 37
38 38 {% block layout_head_title %}
39 Removed <title>{{ page.metaTitle }}</title>
39 Added <title>{{ page.metaTitle|default(shop.name) }}</title>
40 40 {% endblock %}
41 41
42 42 {% block layout_head_stylesheet %}

WooCommerce: recipe document-title-template

Show the before and after (1)
lets the empty custom SEO title fall back to the generated title Example data

functions.php Changes to functions.php: 2 added, 1 removed

Changes to functions.php: 2 added, 1 removed
Line before Line after Change Code
87 87 if ( is_singular() && metadata_exists( 'post', get_the_ID(), '_kb_seo_title' ) ) {
88 88 // VALIDISCAN-SEED document-title: the Contact page has the field saved empty, and an empty
89 89 // string wins over the generated title, so <title> is empty there.
90 Removed return (string) get_post_meta( get_the_ID(), '_kb_seo_title', true );
90 Added $custom_title = trim( (string) get_post_meta( get_the_ID(), '_kb_seo_title', true ) );
91 Added return '' !== $custom_title ? $custom_title : $title;
91 92 }
92 93 return $title;
93 94 }

Magento: recipe document-title-template

Show the before and after (1)
removes the blank search results title and validates Example data

app/design/frontend/Validiscan/fixture/Magento_CatalogSearch/layout/catalogsearch_result_index.xml Changes to app/design/frontend/Validiscan/fixture/Magento_CatalogSearch/layout/catalogsearch_result_index.xml: 0 added, 1 removed

Changes to app/design/frontend/Validiscan/fixture/Magento_CatalogSearch/layout/catalogsearch_result_index.xml: 0 added, 1 removed
Line before Line after Change Code
4 4 <!-- VALIDISCAN-SEEDED document-title: a "hide the SEO title" customisation sets an empty page title,
5 5 so the rendered <title></title> is empty. The visible h1 is set separately by the page.main.title block.
6 6 Fixed variant: <title>Search results for: 'jacket'</title>. -->
7 Removed <title> </title>
8 7 </head>
9 8 <body>
10 9 <referenceBlock name="search_result_list" template="Magento_Catalog::product/list.phtml"/>

Start with a free check of your shop.

Send your shop’s address. Validiscan scans it and replies with every failure the scan can prove, with the page and the code.

Request a free check

Or write to info@validiscan.com.

What the free check covers, and what it leaves out
You send
your shop’s address
Pages scanned
up to 15
You get
every failure the scan can prove, with the page and the code
On your shop
public pages only; no login, nothing bought, no forms sent
What it is not
a full audit

Write to Validiscan

For a free check, give the address of your shop and its platform.

Validiscan uses what you send to answer you. Privacy notice