TS-39: HTML

HTML was simple once. In its early days, the language consisted of a few dozen tags and a handful of attributes, and offered little interactivity or multimedia. Since then, the web has grown from a network of interconnected text documents into an application runtime, and HTML has grown with it. It is now just one of several core languages that make up the web platform, alongside CSS, JavaScript, and data interchange formats such as XML and JSON, plus adjacent formats and protocols (SVG, WebM, WebP, WOFF, RSS, Atom, and more). These are specified across hundreds of documents produced by multiple standards bodies, including the World Wide Web Consortium (W3C), the Web Hypertext Application Technology Working Group (WHATWG), the Internet Engineering Task Force (IETF), and the International Organization for Standardization (ISO).

Not all of modern HTML is useful, and not all of it is safe to use. Some elements and attributes are rarely used and add little value. Some are redundant with other standards. For example, the <nav> element from HTML5, the role="navigation" attribute from WAI-ARIA, and typeof="SiteNavigationElement" from the RDFa implementation of Schema.org, all serve the same purpose. And some things are not supported consistently across target browsers and platforms, so cannot be relied upon in production.

This technical standard defines a working standard for HTML. Think of it as a practical subset of the full language, filtered down to the markup that is useful, non-redundant, and reliably supported, for use in production code. It covers document structure and the allowed-element subset, the document head, text content, tables, hyperlinks, forms and buttons, images and SVG, audio/video and embedded content, scripting conventions, metadata schemas, internationalization, and accessibility. For CSS, see TS-40: CSS; for the web platform’s scripting APIs, see TS-37: Web platform APIs; for broader web-GUI concerns (performance, fonts) beyond markup itself, see TS-18: Web GUIs.

Contents

Fundamentals

Encoding and doctype

Every document MUST be served as UTF-8. The server response MUST include Content-Type: text/html; charset=utf-8, and the document itself MUST additionally declare its encoding as the first child of <head>:

<meta charset="utf-8">

The <meta> declaration is not redundant with the HTTP header. It is what lets the document render correctly when opened as a local file, or served by infrastructure that does not set the header, so it MUST be present regardless of what the server sends.

The document type declaration MUST be <!DOCTYPE html>, and it MUST be the very first thing in the file — column 1, line 1, with nothing before it, not even a blank line or a comment. Any content before the doctype forces the browser into quirks mode, which changes box-model and layout behavior unpredictably.

Source order and document structure

HTML’s primary role is to convey the structure and meaning of content, not its presentation. Source order matters independently of how a page is styled, because it is what a screen reader, a search engine crawler, or a browser with CSS disabled sees. Stripped of all styling, a page SHOULD still read coherently from top to bottom: heading, then the content it introduces; navigation grouped and labeled as navigation; the main content in one identifiable block.

Sectioning elements MUST be used wherever they apply, in preference to a generic <div>:

  • <header> for introductory content — typically a page or section’s heading, logo, and top-level navigation.
  • <nav> for a block of primarily navigational links. A <nav> SHOULD carry a heading (which MAY be visually hidden) where a page has more than one, so that a screen reader user can distinguish "primary navigation" from "pagination" or "breadcrumbs" without inspecting each one’s content.
  • <main> for the document’s primary content, unique per page.
  • <article> for a self-contained piece of content that would make sense distributed or syndicated on its own — a blog post, a comment, a product card.
  • <section> for a thematic grouping of content that is not self-contained enough to be an <article>. A <section> SHOULD contain a heading; a generic grouping with no heading and no distinct semantic role is a <div>, not a <section>.
  • <aside> for content tangentially related to the surrounding content — a pull quote, a related-links box, a sidebar.
  • <footer> for closing content — typically metadata about its nearest ancestor sectioning element, such as authorship, copyright, or related links.

<figure> is not limited to images — see Figures — and is the correct wrapper for any content referenced from the surrounding prose that could be moved without breaking the flow of that prose: a code sample, a table, a video, or a quotation, each with an optional <figcaption>.

A generic <div> or <span> remains the right choice where none of the above apply: a purely presentational wrapper introduced for styling or scripting, with no semantic role of its own.

Allowed elements

This standard defines a working subset of HTML: elements that are useful, non-redundant with another element already in the set, and reliably supported. An element not in this list MUST NOT be used, even where it is valid HTML, unless this standard is extended to permit it.

Sectioning and grouping

html, head, body, header, nav, main, article, section, aside, footer, div, figure, figcaption, hr.

Text content

h1h3, p, ul, ol, li, dl, dt, dd, pre, blockquote, address.

Inline text

a, b, bdi, bdo, br, code, data, em, i, mark, small, span, strong, sub, sup, time.

Tabular data

table, caption, thead, tbody, tfoot, tr, th, td.

Forms

form, fieldset, legend, label, input, textarea, select, option, optgroup, button.

Media and embedded content

img, svg (and its own conforming child elements — see SVG as the default vector format), audio, video, source, track, iframe, embed, picture.

Scripting and metadata

script, noscript, template, meta, link, title, style, base.

This list deliberately excludes elements that are deprecated (acronym, applet, center, font, marquee, strike, tt), that have unreliable or inconsistent browser support for their intended behavior (dialog, menu, menuitem, keygen), or that this standard judges not worth the added complexity for a working-standard subset (abbr, dfn, ins, del, s, u, kbd, samp, var, wbr, meter, progress, details, summary, area, map). Two exclusions are worth calling out specifically because earlier drafts of this standard’s source material disagreed on them:

  • <dl>/<dt>/<dd> is permitted. It is the correct element for genuinely paired term/description content — a glossary, a metadata list, a FAQ — and excluding it in favor of <ul> throws away real semantics for no benefit. It MUST NOT be used as a general-purpose layout list; use <ul> or <ol> for anything that is not a term/description pairing.
  • <b>, <i>, and <small> are permitted, each for a specific semantic meaning that is not covered by <strong> or <em> — see Inline text elements. They MUST NOT be used purely for visual styling; that is what CSS is for.

<caption> is likewise permitted — see Tables.

Coding style

  • Documents MUST be well-formed and MUST validate against the W3C HTML validator.
  • Tag names and attribute names MUST be lowercase.
  • Attribute values MUST use double quotes. Single quotes are reserved for JSON embedded in an attribute value, so that the JSON’s own double quotes do not need escaping.
  • Boolean attributes (disabled, checked, required, and similar) MUST be written valueless — <input disabled>, not <input disabled="disabled">.
  • Every element MUST be explicitly closed. Void elements (br, img, input, hr, meta, link) MUST use the explicit self-closing form with a single preceding space: <br />.
  • <html>, <head>, and <body> MUST always be written explicitly, even though HTML’s parsing rules would infer them.
  • Indentation MUST use four spaces, never tabs. Each block-level element MUST start on its own line.
  • Attributes on the same element SHOULD be written in alphabetical order, to make a long attribute list scannable and diff-friendly.
  • class MUST be used exclusively as a CSS styling hook. It MUST NOT be queried by JavaScript to select elements — see Data attributes for the attribute JavaScript MUST use instead.
  • id is reserved for hash-fragment targets and for DOM relationships that require a unique reference (for/id, aria-labelledby, and similar). An id MUST be lowercase, hyphen-delimited ASCII, and SHOULD stay under around 15 characters. An id that exists purely to express a relationship between two elements SHOULD be prefixed with the node name of the element that owns it, e.g. input-email, select-country.
  • The literal characters &, <, and > MUST be escaped as &, <, and > wherever they appear in text content or attribute values, other than inside markup itself. Inside a double-quoted attribute value, a literal " MUST be escaped as ". Any other special character SHOULD be written as its literal UTF-8 character, not as a numeric or named entity — the file’s UTF-8 encoding makes entities unnecessary for anything beyond &/</>/". The exception is a zero-width space (), which MAY be inserted before punctuation in a long unbroken string (a URL or an email address) to give the browser a legal line-break opportunity.
  • Use the hidden boolean attribute to remove an element from both the visual rendering and the accessibility tree. It is preferred over a CSS display: none or visibility: hidden rule for content that is not yet relevant, because it communicates that removal semantically rather than only visually — see Visibility for the cases where CSS is the correct tool instead.

Document head

The <head> MAY contain only <meta>, <link>, <title>, <style>, <script>, and <base>. A <noscript> element MAY also appear in <head>, but only to declare a fallback <meta http-equiv="refresh"> or similar no-script directive — content intended for a no-script body belongs in <body>, not <head>, since <head> content is never rendered.

The document’s two mandatory <meta> tags — charset and viewport — MUST be the first two children of <head>, in that order, before <title> or anything else:

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Page Title</title>
    ...
</head>

The charset declaration MUST come first because the browser has to know the document’s encoding before it can correctly parse anything after it, including the rest of <head>.

Base URL

A <base> element is strongly RECOMMENDED. It fixes the document’s base URL for every relative URL on the page — links, image sources, form actions — which removes an entire class of bugs where a relative URL resolves correctly on one route but not another (for example, after client-side routing changes the browser’s current path without a full navigation).

<base href="https://example.com">

The href MUST be written as scheme://domain, with no trailing slash and no path. A trailing slash or a path segment on <base> changes how every relative URL on the page resolves, in a way that is easy to get wrong and hard to spot in review; keeping <base> to the origin only avoids the ambiguity entirely. Relative URLs elsewhere on the page follow the convention set out in Hyperlinks.

Title

Every document MUST have a <title>. It has no visual presence in the page itself, but it is what appears in the browser tab, the browser history, a search result, and any bookmark — and it is the first thing many screen reader users hear when a page loads, so it MUST communicate what the page is on its own, without relying on visible page content for context.

A title SHOULD stay within roughly 55–60 characters, since search engines and browser tabs truncate longer titles, usually mid-word, which reads worse than a shorter, deliberately chosen title.

The title convention differs by product type:

  • A content website’s title SHOULD follow "Page Title" with no fixed boilerplate suffix that eats into the character budget on every page.
  • A web application’s title SHOULD use "Page Title – App Name" (an en dash, not a hyphen, with a space on each side) so the application’s identity is still visible when several of its tabs are open side by side.

Where a page’s <title> includes an application name, the same name SHOULD be given in an application-name <meta> tag (see Meta tags), so a browser or platform surfacing the app’s identity outside the tab bar (a pinned-tab tooltip, an OS taskbar) has it without re-parsing the title string.

A single-page application whose state changes without a full navigation MUST update document.title to reflect that state change — a title left static after the visible content has moved on misleads a screen reader user relying on the title as their orientation cue, and misleads browser history and bookmarks equally.

Title case is not used. A title SHOULD use sentence case, capitalizing only the first word and any proper nouns.

Meta tags

The following <meta> tags are RECOMMENDED, over and above the mandatory charset and viewport:

application-name

The application’s short name. Web applications only — a content website has no equivalent concept.

description

A one-sentence summary of the page, 120–150 characters, written as a full sentence rather than a keyword fragment. REQUIRED on every publicly indexed page. It MUST be unique per page: a description copied across pages, or omitted so a search engine falls back to scraping arbitrary body text, both produce a worse search-result snippet than a deliberately written one.

keywords

Carries no ranking weight with any major search engine today. It MAY be included as an internal, human-readable reference to a page’s intended topic, but MUST NOT be relied on for SEO.

author

The document’s author or owning organization, where relevant.

referrer

Controls what Referer information the browser sends when a user follows a link away from the page. This standard does not mandate a value, but whatever value is chosen MUST be a static, deliberate policy decision — not injected dynamically per request, which makes the page’s referrer behavior unauditable from its own source.

robots / googlebot

Controls indexing and link-following for the page — index/noindex and follow/nofollow, independently combinable. Most pages need neither, because indexing and following are the default; use noindex explicitly on pages that MUST NOT appear in search results (an internal search-results page, a duplicate of canonical content elsewhere) and on error pages such as a 404.

Content-Security-Policy

A <meta http-equiv="Content-Security-Policy"> tag MAY set a page’s CSP where the server response cannot set the equivalent HTTP header. See Content Security Policy for what the policy itself should contain.

The full list of registered <meta name> extensions is maintained by the WHATWG; only the values above are commonly useful for a production document — do not add a <meta> tag speculatively on the strength of it appearing in that list.

Linked resources

<link> declares a relationship between the document and an external resource. The following rel values are in regular production use:

  • stylesheet — the document’s CSS.
  • manifest — the web app manifest, for installable web applications.
  • canonical — the authoritative URL for this content, where the same or substantially similar content is reachable at more than one URL (with and without a tracking query parameter, for example). REQUIRED wherever that ambiguity exists, so a search engine consolidates ranking signal onto one URL instead of splitting it.
  • alternate — an alternate representation of the page: a translated version (paired with hreflang), or a feed (paired with type, e.g. application/rss+xml).
  • icon — the favicon and any additional sizes/formats for different platforms.

Paginated content SHOULD use rel="prev" and rel="next" to link sequential pages; first and last MAY also be given where the sequence has a fixed length. These help both a search engine and assistive technology understand the page’s position within a series independently of any in-page "Next" link the user sees.

<link rel="prefetch"> and <link rel="preload"> MAY be used to hint the browser toward a resource it will need imminently. Both are advisory — the browser may ignore either under memory or bandwidth pressure — so neither is a substitute for the resource’s own caching headers.

Text content

Headings

Headings convey document structure, not visual emphasis — a heading MUST be chosen because the content beneath it is a genuine subsection, never because a piece of text needs to look bigger. Presentation is CSS’s job; resizing a heading to get a visual effect, or using a heading to avoid writing a CSS rule for bold, large text, both misuse the element.

  • Headings MUST be ordered hierarchically. A page MUST NOT skip a level on the way down (an <h1> followed directly by an <h3>, with no <h2> in between).
  • Two headings of the same level MUST NOT appear consecutively with no intervening content — a heading with nothing under it before the next heading is a sign the content is missing, or the heading is superfluous.
  • This standard restricts documents to <h1><h3> — see Allowed elements. Three levels are enough for the vast majority of content; a document that seems to need a fourth level is usually a sign the content itself should be split across more pages, or organized with fewer, better-chosen sections.
  • More than one <h1> is permitted, provided each is the heading of its own sectioning element (<article>, <section>) — HTML’s outline model treats each sectioning element as starting a new heading context. A page that is not composed of independent sectioned regions SHOULD still use exactly one <h1>, for its own top-level title.
  • A heading MAY be wrapped in a hyperlink where the heading text itself is the link (a blog index linking each post’s title). No other inline element is permitted inside a heading — a heading is a plain-text structural label, not a place for a <button>, an icon, or a <span> used for partial styling.

Lists

HTML has three list types, each for a different editorial relationship between its items:

  • <ul> — an unordered list, for items whose relative order carries no meaning (a set of navigation links, a set of features).
  • <ol> — an ordered list, for items whose sequence is meaningful (steps in a procedure, a ranking). The numbering MUST NOT be hidden with CSS — if the order does not need to be visible, the content is not really ordered, and <ul> is the correct element.
  • <dl> — a description list, for term/description pairs (see Allowed elements for why this standard permits it where some earlier drafts of this material did not). Every <dt> MUST be paired with at least one following <dd>.

Both <ul> and <ol> MUST contain at least one <li> — an empty list is not valid content. Nesting SHOULD NOT go deeper than three levels; content that needs a fourth level of nested list is usually better restructured with headings.

<ol> accepts a type attribute (type="a", type="i", and similar) for a nested list’s numbering style, where a numeral-only default would be visually ambiguous against its parent list’s own numbering.

No block-level element other than a nested <ul> or <ol> MAY appear directly inside a <ul>/<ol> beyond the <li> itself. Keep list item text short — a list item that runs to several sentences of prose is usually better written as its own paragraph under a heading.

Inline text elements

The permitted inline elements (see Allowed elements) each carry a specific meaning:

em

Stress emphasis that changes the meaning of the sentence it is in — the word a reader would naturally stress aloud. Not a general-purpose italics tool.

i

Text in an alternate voice or mood without stress emphasis — a technical term on first use, a foreign-language phrase, a ship’s name. Where the alternate-voice case does not apply, <em> or plain text is the better choice.

strong

Strong importance, seriousness, or urgency — content the reader MUST NOT skim past. Not a general-purpose bold tool.

b

Text that is stylistically offset without conveying extra importance — a keyword in a summary, a product name in a review. Where the extra-emphasis case does apply, <strong> is the correct choice instead.

mark

A run of text highlighted for reference relevance in the current context — a matched search term, for example — rather than for its own importance.

small

Side comments and small print — a disclaimer, a copyright line. It signals a genuinely different content register, not a bare font-size decrease; apply an actual font-size decrease with CSS.

span

The inline equivalent of <div> — a wrapper with no semantic meaning of its own, used only for styling or scripting hooks.

code

A fragment of literal code, filename, or computer output. Combined with <pre> (see Pre-formatted text) for a multi-line block.

data

Machine-readable content paired with a human-readable value, via the value attribute — for example, a product name in the text content with its SKU in value.

time

A date or time, machine-readable via the datetime attribute.

a, bdi, bdo, br, sub, sup

Covered in Hyperlinks, Internationalization, and their own uses respectively — a line break, and subscript/superscript for genuinely subscripted or superscripted characters (a footnote marker, a chemical formula), not for visual positioning.

abbr, dfn, ins, del, kbd, samp, var, wbr are excluded from this standard’s allowed-element set (see Allowed elements) — each has a narrow, rarely needed use, and the working-standard subset favors the smaller set above.

Horizontal rules

<hr> represents a paragraph-level thematic break within content — a scene change, a shift in topic within a single section. It MUST NOT be used purely for visual decoration (a horizontal line for its own sake — that is a CSS border), and MUST NOT be placed immediately before a heading or between two sectioning elements, where the sectioning boundary itself already marks the break.

Pre-formatted text

<pre> preserves whitespace and line breaks exactly as written, for content whose structure is inherently typographic rather than semantic — source code (typically paired with <code>), ASCII art, or any text where the line breaks and spacing are part of the content’s meaning. It MUST NOT be used as a shortcut to avoid writing proper markup for structured content that has its own semantic elements available.

Addresses

<address> is a sectioning element for contact information for its nearest <article> or the document as a whole — not for any physical address that happens to appear in the content. An address printed as part of unrelated body text (a company’s registered office quoted in a paragraph) does not belong in <address>; the element is reserved for contact information about the content’s author or owner.

Tables

<table> MUST be used only for genuinely tabular data — content with a real two-dimensional relationship between rows and columns, such as a comparison matrix or a schedule. It MUST NOT be used for page layout; CSS grid and flexbox are the tools for visual layout, and a layout table breaks the reading order for a screen reader, which announces row/column structure that has no relationship to the actual content.

A conforming table MUST use explicit structural elements rather than relying on row position alone:

<table>
    <caption>Quarterly revenue by region</caption>
    <thead>
        <tr>
            <th scope="col">Region</th>
            <th scope="col">Q1</th>
            <th scope="col">Q2</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <th scope="row">EMEA</th>
            <td>$1.2M</td>
            <td>$1.4M</td>
        </tr>
    </tbody>
    <tfoot>
        <tr>
            <th scope="row">Total</th>
            <td>$1.2M</td>
            <td>$1.4M</td>
        </tr>
    </tfoot>
</table>
  • <thead>, <tbody>, and <tfoot> MUST be used explicitly rather than left to be inferred from row position — the explicit grouping is what a screen reader uses to announce "column header" versus a data cell, and what lets <tbody> alone scroll independently in a long table.
  • <th> MUST be used for every heading cell, whether it heads a column (scope="col") or a row (scope="row"). A <td> styled to look like a heading carries no heading semantics to assistive technology.
  • <caption> is permitted and RECOMMENDED — see Allowed elements for why this standard includes it where some earlier drafts of this material excluded it. It MUST be used for the table’s own title or a brief note on its source, not as a substitute for a heading in the surrounding document structure; where the table needs a heading the reader can link to or that appears in a page outline, use a normal heading before the table in addition to, or instead of, a <caption>.
  • The summary attribute is obsolete and MUST NOT be used; <caption> and properly scoped <th> elements supersede it.
  • A border attribute MUST NOT be used — table borders are a CSS concern.

The anchor element

<a> is a block-or-inline hybrid, valid in either context, and is the only element this standard permits to wrap another block-level element for the purpose of making a whole card or region clickable.

  • href is mandatory on every <a> used as a link, and MUST resolve to a real resource. An <a> with no href, or one that points nowhere, is not a link — either give it a real destination or use a <button> instead, per JavaScript-driven links.
  • Internal links MUST be written relative to the document’s base URL, not as absolute URLs, so the same markup works unchanged across environments (local, staging, production).
  • title is advisory only — it is not reliably exposed to assistive technology or to touch input, and MUST NOT carry information the reader needs to use the link. Put anything essential in the visible link text instead.
  • Other attributes in regular use: download (suggests a filename for save-as, but unreliable across browsers for cross-origin URLs — prefer a Content-Disposition HTTP header where the download behavior is important), hreflang (the linked resource’s language, primarily for rel="alternate" translations), rel (see below and Microformats), target (see below), and type (the linked resource’s MIME type, as an advisory hint).

For URL construction and design more broadly — path structure, query parameters, trailing slashes — see TS-63: URL design; this section covers only how a link is marked up, not how the URLs it points to are designed.

Every URL used in an <a href> MUST conform to RFC 3986. A literal space in a URL MUST be percent-encoded as %20, never left literal or replaced with a +, which has a different meaning in a query string.

  • An internal link SHOULD be relative rather than absolute, resolved against the page’s base URL. A link to the site’s homepage SHOULD use ./ rather than an empty string or the domain root written out.
  • ../ (a parent-relative path) MUST NOT be used — it makes a link’s target depend on where the linking page itself sits in the URL hierarchy, which breaks silently if either page moves.
  • A link that includes a hash fragment MUST still resolve to the correct canonical page without the fragment — the fragment identifies a location within the page, not a substitute for the page’s own correct path.
  • An external link SHOULD carry rel="external" so it can be styled or handled distinctly (opened in a new tab, flagged visually) without relying on brittle URL-matching logic.
  • A query parameter that does not change what the user sees SHOULD be avoided — every additional optional parameter is another URL variant for a search engine to index separately and another case for rel="canonical" to account for.
  • A published URL MUST NOT expose a username, password, or non-standard port — https://user:pass@example.com credentials embedded in a URL are a security anti-pattern; see TS-52: Security and secrets management.

Every link rendered on initial page load MUST resolve to a useful resource without requiring JavaScript to run first — a link whose real destination is only known after a script executes fails for any client that does not run that script, including a search engine crawler in some configurations.

Where a click needs to trigger application behavior rather than a plain navigation, and that behavior still corresponds to a genuine URL (a client-side-routed page, for example), the <a>’s `href MUST still point at that real URL, so that opening it in a new tab, right-clicking it, or following it with JavaScript disabled all still work; the click handler is an enhancement on top of a working link, not a replacement for one.

  • href="javascript:…​" MUST NOT be used.
  • An inline event-handler attribute (onclick, and similar) MUST NOT be used — see Progressive enhancement for how a handler should be attached instead.
  • Where a click genuinely does not correspond to any URL — it triggers an in-page action with no navigable destination, such as opening a modal — the correct element is a <button>, not an <a> with no real href. See Buttons.

mailto: links SHOULD generally be avoided in favor of a contact form, which does not expose the address to scraping and gives the sender a consistent, validated submission path.

A tel: link is acceptable, and SHOULD use the international format: a leading +, the country code, then the national number with no leading trunk zero and no internal spaces, hyphens, or other punctuation — tel:+442012345678, not tel:+44 (0)20 1234 5678. The visible link text MAY differ from the tel: value and be formatted however is conventional for the reader’s locale; only the href value has this strict format requirement.

Forms

The form element

<form> MUST encapsulate every control it submits — a control outside its <form> (associated only via the HTML5 form attribute pointing at the form’s id) is easy to lose track of during a refactor, so this standard prefers the simpler, self-contained structure of every submitted control nested inside its own <form>.

  • action and method SHOULD be set explicitly, even though both default to the current URL and GET respectively — an explicit value documents the form’s behavior at the point of definition rather than relying on the reader to know the defaults.
  • An action targeting the same origin SHOULD be a relative URL, per Hyperlinks; a cross-domain action MUST be absolute.
  • id is preferred over name for identifying the form itself in CSS or JavaScript, consistent with Coding style. name remains necessary on individual controls, since it is what the browser uses as the submitted field key.
  • enctype="multipart/form-data" MUST be set whenever the form contains a file input, and only works with method="post" — a file input inside a GET form, or without the correct enctype, silently fails to submit the file.

Validation and input attributes

Native HTML form validation and autofill are unreliable in appearance and behavior across browsers, and this standard therefore prefers a consistent, custom-styled validation experience over the native one:

  • novalidate SHOULD be set on the <form> to suppress the browser’s own validation UI, and validation logic implemented in JavaScript instead — see TS-21: HTTP APIs for how that client-side validation relates to the server-side validation the API MUST still perform independently.
  • spellcheck="false" SHOULD be set on any field where the browser’s spellcheck underline is unhelpful noise — a code field, a username, a postal code.
  • autocomplete="off" SHOULD be set at the <form> level for a form where browser-remembered values are actively unhelpful (a search box that should start empty each time), and autocomplete="on" (or a specific autofill token, e.g. "email", "given-name") set per control where the browser’s autofill genuinely helps the user, such as a standard address or payment form.
  • autocapitalize="none" MAY be set to prevent iOS Safari’s non-standard automatic capitalization on a field where it is unhelpful (a username, an email address) — this attribute is a Safari-specific extension, not part of the HTML standard, but is widely supported as a no-op elsewhere.

Passwords are a specific case worth calling out: users MUST NOT be prevented from pasting into a password field. Blocking paste (historically done to force manual re-entry, on the mistaken belief it improved security) actively harms users of password managers and makes strong, randomly generated passwords harder to use, which the UK National Cyber Security Centre has documented as a net security loss, not a gain.

Input controls

The following <input> types, plus <select> and <textarea>, are the permitted control set:

checkbox, radio

value is mandatory on both, even where there is only one option, because it is what gets submitted. Both MUST be wrapped in a <label> (see Labels, fieldsets, and legends) so that clicking the label text toggles the control. An unchecked checkbox submits nothing at all, not an empty or false value — a form that needs to distinguish "unchecked" from "not shown" MUST pair the checkbox with a hidden input carrying the unchecked-state default.

email, search, tel, url, number, date

These specialized types delegate basic format validation and, on mobile, an appropriate on-screen keyboard, to the browser. They degrade gracefully to a plain text input in a browser that does not recognize the type, so there is no compatibility cost to using them wherever the semantic type applies.

range and datetime-local are more inconsistently implemented across browsers and MUST NOT be relied upon as the sole means of input — provide an equivalent text-based fallback or a custom control instead.

Where a field expects a specific, limited format, offering the user a constrained choice (a <select>, a set of radio buttons) is preferable to free text wherever the set of valid values is small and known in advance — it removes an entire class of format errors rather than validating them after the fact.

password

MUST NOT have a value set from the server — a pre-filled password field either leaks the value into the page’s HTML source or, worse, is never the correct value the user intended.

text

The default. maxlength limits input length; size sets the control’s visible width in characters but is a layout no-op in any form styled with CSS, so size SHOULD be omitted in favor of a CSS width.

file

Requires method="post" and enctype="multipart/form-data" on the parent <form> — see The form element. accept is an advisory filter on the file picker only, not a validation guarantee, so the server MUST still verify the uploaded file’s actual type. multiple permits selecting more than one file in a single control.

hidden

Carries a value the user does not see or edit, submitted along with the rest of the form.

select

SHOULD be used once a control has around 12 or more options — below that, a set of visible radio buttons is usually more usable, since it does not require an extra interaction to reveal the choices. The multiple attribute on <select> produces a poorly understood, hard-to-style multi-select widget and SHOULD be avoided in favor of a set of checkboxes.

textarea

cols and rows are unitless integers, not CSS lengths. cols SHOULD be overridden with a CSS width: 100% (or an explicit width) rather than relied on for layout; rows sets a starting height, which CSS height (or resize) can then adjust. A reset-type button paired with a <textarea> or any other form is discouraged — see Buttons.

Labels, fieldsets, and legends

Every form control MUST have an associated <label>, connected either by wrapping the control inside the <label> or by a matching for/id pair. A control with no programmatically associated label is unusable with a screen reader and gives no larger click/tap target than the control itself.

Related fields MUST be grouped in a <fieldset> wherever the grouping is meaningful — a set of radio buttons for one question, the address fields of a shipping form. <fieldset> MUST be used even where the group contains only a single control, for consistency and because groups routinely grow a second field later. A <fieldset> with more than one control SHOULD have a <legend> naming the group, which a screen reader announces as the group’s own label, distinct from each individual control’s own <label>.

Buttons

<button> MUST always carry an explicit typetype="submit" for a button that submits its enclosing <form>, or type="button" for any other push-button behavior handled by JavaScript. A <button> with no type attribute defaults to type="submit", which is a common source of forms being submitted accidentally by a button that was only meant to trigger some other in-page behavior.

  • type="reset" MUST NOT be used, on a <button> or an <input>. A reset button discards user-entered data with one click and no confirmation, which is far more often a source of accidental data loss than a useful feature.
  • <input type="button"> and <input type="submit"> MUST NOT be used — <button> is the equivalent element, and additionally allows rich content (an icon plus text) inside it, which <input> does not.
  • name and value are meaningful only on a submit button, where they become part of the submitted form data (useful for a form with more than one possible submit action, distinguished by which button was pressed). They serve no purpose on a type="button" button.
  • A button that triggers an action with in-flight latency (a network request) SHOULD be disabled for the duration of that action, to prevent a double-click from submitting the action twice.
  • autofocus MAY be used on the button that represents the default, most-likely next action in a view — a dialog’s primary action, for example.
  • autocomplete="off" MAY be set on a button where the browser’s autofill has been observed re-triggering an unwanted saved action; this is rarely needed in practice.

See JavaScript-driven links for the corresponding rule on <a>: an element that triggers behavior with no real navigable destination is a <button>, never a link with an empty or fake href.

Images

Figures

Any graphic referenced from the surrounding prose — a chart, a photograph, a code sample, a quotation, a table, or a video — MUST be wrapped in <figure> if it is content the surrounding text refers to, rather than purely decorative. <figcaption> is RECOMMENDED, and where present MUST sit at the bottom of the figure, describing what the figure shows. <figure> accepts at most one <figcaption>.

A caption is not a substitute for an image’s alt text — see The img element. The two serve different readers: alt is what stands in for the image when it cannot be seen or loaded at all, and the caption is supplementary context shown alongside the image for every reader, sighted or not.

The img element

<img> MUST always carry src, width, height, and alt. ismap and usemap MUST NOT be used — see Image maps for why that mechanism is obsolete.

  • width and height MUST exactly match the image’s real pixel dimensions. This is what lets the browser reserve the correct amount of layout space before the image has finished downloading, preventing a content reflow ("layout shift") once it loads.
  • alt MUST describe the image’s purpose in context, not literally describe its pixels — "Line chart showing revenue up 40% in Q2", not "A blue line going up and to the right." It SHOULD stay under roughly 50 characters where possible; longer context belongs in a <figcaption> or the surrounding prose, not crammed into alt.
  • An image that is purely decorative — contributing no information a screen reader user needs — MUST still carry alt="" (an empty but present attribute). Omitting alt entirely, rather than setting it empty, causes some screen readers to announce the image’s filename instead, which is worse than announcing nothing.
  • Three raster/vector formats cover production use: JPEG for photographic content, PNG for content needing transparency or sharp edges (UI screenshots, diagrams with flat color), and SVG — embedded via <svg> directly, per Vector graphics, in preference to <img src="*.svg"> — for anything that is actually vector artwork (icons, logos, line art).

Raster image formats

BMP, GIF, ICO, JPEG, and PNG are universally supported. JPEG and PNG cover the overwhelming majority of production use — JPEG for photographic content where some lossy compression is acceptable, PNG where transparency or lossless sharp edges are required. Animated PNG (APNG) is supported widely enough to use for short looping animations in place of GIF, which gives visibly worse quality at a comparable file size.

WebP offers meaningfully better compression than JPEG or PNG at equivalent visual quality, but MUST be served with a <picture>/<source> fallback to JPEG or PNG (see Responsive images) rather than as the sole <img src>, for the small remaining share of clients without WebP support.

Image optimization

Every image MUST be compressed to the point just short of perceptible quality loss — not merely "reasonably small," but tuned per image. As a general target, an image intended for a typical content page SHOULD be under 25 KB where the source material allows it without visible degradation; a genuinely detailed photograph will legitimately exceed that, and forcing it under the target at the cost of visible artifacts is the wrong tradeoff.

  • Export at the size the image is actually rendered at, not larger — a 4000px source photo displayed at 400px wide wastes bandwidth on detail no viewport will show, on top of whatever the browser’s own downscaling costs in decode time.
  • DPI/PPI metadata is irrelevant to how a browser renders an image; only the pixel dimensions matter. Do not rely on a DPI setting to control perceived size or quality.
  • Export in RGB, never CMYK — CMYK images render incorrectly, or fail to render, in a browser context.
  • Where further size reduction is needed and a small amount of blur is an acceptable tradeoff, a slight gaussian blur before JPEG compression can reduce file size noticeably, because JPEG compresses smooth gradients more efficiently than sharp detail.

Serving images

  • File extensions MUST be lowercase.
  • The server MUST send the correct Content-Type for the image format being served.
  • Images SHOULD be served from a separate, cookie-free domain (or subdomain) where the main site sets cookies, so that every image request does not carry the full cookie header for no benefit.
  • Cache headers (Cache-Control, Expires) SHOULD set a long cache lifetime — six months or more — for images, which rarely change in place. Where an image genuinely does need to change, rename the file (or add a content hash to the filename) rather than relying on cache invalidation of the old URL, which is unreliable across intermediate caches and CDNs.
  • Many small images (icons, small UI graphics) MAY be inlined as data URIs directly in CSS or HTML, trading one HTTP request for a larger inline payload — worthwhile below a certain size and request-count threshold, and worth measuring rather than assuming for a given page.

Responsive images

A responsive image serves an appropriately sized (and sometimes differently art-directed) file depending on the viewport and the device’s pixel density, rather than one fixed file scaled by CSS.

The simplest and RECOMMENDED mechanism is srcset with x descriptors, for serving a higher-resolution file to a high-density display without changing which image is shown:

<img
    src="photo.jpg"
    srcset="photo.jpg 1x, photo@2x.jpg 2x"
    alt="…">

Where different viewport sizes need genuinely different crops or compositions of the same subject — "art direction," not just resolution — <picture> with multiple <source> elements, each with a media condition, is the correct tool:

<picture>
    <source media="(min-width: 800px)" srcset="wide-crop.jpg">
    <source media="(min-width: 400px)" srcset="medium-crop.jpg">
    <img src="narrow-crop.jpg" alt="…">
</picture>

<source> elements MUST be listed in the order they should be evaluated — the browser uses the first matching media condition, so a more specific condition MUST come before a more general fallback.

An alternative mechanism — srcset with w descriptors paired with a sizes attribute, where the browser itself picks the best-fit file based on the rendered layout width — exists and is valid, but its sizes value requires expressing the image’s rendered width as a function of viewport width (often with calc()), which is intricate to get right and expensive to keep in sync as a layout changes. This standard generally advises against it in favor of the simpler x-descriptor or <picture> patterns above, reserving w-descriptor sizes for cases neither of those covers.

Image maps

<area>/<map> client-side image maps remain valid, standard HTML and are still supported by every current browser, but are largely obsolete — the interactive-region use case they serve is better met today by SVG (each shape as its own element, styleable and independently focusable) or by a canvas-based implementation for anything more dynamic. New work SHOULD NOT use image maps.

Vector graphics

SVG as the default vector format

SVG is the only vector graphics format the web platform supports natively, and SHOULD be used for every graphical UI component that is not a photograph — icons, logos, illustrations, diagrams, and charts alike. It is preferred over an icon font (Font Awesome and similar) or a sprite sheet: an icon font repurposes a text-rendering pipeline for graphics, which brings font-loading failure modes and accessibility problems (an icon font glyph read aloud as its fallback character) that an inline SVG does not have.

Linking versus inlining

An SVG can be referenced like a raster image (<img src="icon.svg">, a CSS background-image) or embedded directly in the HTML as <svg>…​ </svg>. Inlining is RECOMMENDED:

  • It costs one fewer HTTP request per graphic.
  • An inlined SVG’s internal elements are addressable and styleable from the page’s own CSS and JavaScript — a linked SVG’s internals are opaque to the linking document.
  • An inlined SVG can carry <title>/<desc> that assistive technology reads directly as part of the page’s own accessibility tree; a linked SVG depends on the surrounding <img alt> instead, which cannot expose the SVG’s own internal structure.

A linked SVG (where linking is chosen) MUST be self-contained — no external stylesheet or script reference inside the SVG file — since a browser’s handling of an external resource reference from within a linked SVG is inconsistent. <object> is a workaround for accessing a linked SVG’s internals from the outer document, at the cost of extra markup and its own quirks, and is only worth reaching for where inlining is not practical.

Where more than one inlined SVG on the same page defines an id (commonly on a <clipPath>, a <mask>, or a gradient definition), every id MUST be unique across the whole document, not just within its own SVG — two inlined SVGs both defining id="icon-mask" collide, and the second silently overrides the first. Namespacing generated IDs per component, or rendering each reusable icon inside a Web Component’s shadow DOM (which scopes IDs to that shadow tree), both avoid the collision at scale.

A linked SVG (the non-default case above) MUST be served with the file extension .svg and the image/svg+xml MIME type.

SVG attributes

  • viewBox MUST be treated as mandatory. It defines the SVG’s internal coordinate system independently of its rendered size, which is what lets the graphic scale cleanly via CSS.
  • width and height are likewise mandatory on the root <svg> element — older browser versions (Internet Explorer 9–11 in particular) fail to render an SVG with no explicit dimensions even where viewBox is present.
  • x and y position an SVG (or a nested <svg>) within its parent coordinate system, where relevant.
  • version="1.1" SHOULD be kept for maximum compatibility, even though SVG 2 is the current specification; SVG 2 does not introduce a version attribute value of its own, and 1.1 remains the safe, broadly supported declaration.
  • xmlns="http://www.w3.org/2000/svg" is mandatory on the root element. xmlns:xlink is additionally required only where the SVG uses an xlink:href reference (a <use> element, for example) rather than the SVG2 bare href.
  • <title> and <desc> are the SVG equivalent of an <img>’s `alt text, and SHOULD be included on any SVG conveying real information — <title> for a short accessible name, <desc> for a longer description where needed.
  • role="presentation" (or aria-hidden="true") SHOULD be set on an inlined SVG that is purely decorative — an icon that duplicates adjacent visible text — so assistive technology does not announce it as a separate, unlabeled graphic.

SVG filters

SVG filter primitives (<feGaussianBlur>, <feColorMatrix>, and others) apply blur, color manipulation, and compositing effects directly within the SVG. They are supported in every current browser, with the historical exception of Internet Explorer 9 and earlier and Android 4.4 and earlier — not a live constraint for a production standard targeting current browsers, but worth knowing if a project has a legacy support requirement.

SVG animation

Three independent mechanisms can animate SVG content:

  • Direct DOM scripting — manipulating attribute or style values via JavaScript, giving full programmatic control at the cost of writing the animation logic by hand.
  • CSS transitions and animations — the same CSS mechanisms used elsewhere on the page apply directly to SVG properties, and are the RECOMMENDED default for straightforward animations, consistent with the general preference for CSS-driven animation over JavaScript (see Progressive enhancement).
  • SMIL (<animate>, <animateTransform>, and related elements) — animation declared natively within the SVG markup itself. SMIL has inconsistent and, in some browsers, deprecated support, and SHOULD NOT be relied on for new work; use CSS animation instead.

A JavaScript animation library (Snap.svg, svg.js, Velocity.js, and similar) MAY be reached for where an animation’s complexity genuinely exceeds what CSS can express — coordinated multi-element sequences with runtime-computed values, for example — but is unnecessary overhead for anything CSS transitions or animations already handle.

SVG keyboard navigation

An inlined SVG that contains interactive elements (a clickable icon button rendered as SVG shapes, for example) is subject to the same keyboard-accessibility requirements as any other interactive control — see Keyboard navigation in the accessibility section. A purely decorative SVG MUST NOT be focusable at all; where a browser’s default behavior makes an inlined SVG focusable regardless of its content, focusable="false" (or tabindex="-1" combined with aria-hidden="true") MUST be set to remove it from the tab order.

Audio, video, and embedded content

Audio and video

<audio> and <video> MUST be treated as block-level content in layout, even though <video> in particular is sometimes reached for as an inline decorative element.

  • autoplay and loop MUST NOT be used. Playback MUST start only on an explicit user action — autoplaying media is disruptive, and major browsers increasingly block autoplaying audio outright regardless of what the markup requests.
  • muted is not a reliable substitute for the above — some browsers will autoplay muted content, but this MUST NOT be relied upon as a workaround for the autoplay rule; the rule is about not starting playback without user action, not specifically about audible playback.
  • preload is advisory only ("none", "metadata", or "auto") — the browser may override it under bandwidth or data-saving constraints, so it MUST NOT be relied upon as a guarantee of prefetch behavior.
  • <track kind="subtitles"> (WebVTT) SHOULD be provided for any video with spoken content, for accessibility. Older browser versions (Internet Explorer 9 in particular) have gaps in <track> support, but this MUST NOT be used as a reason to omit captions — the standard’s target browsers support it, and captions benefit every viewer in a sound-off context, not only Deaf and hard-of-hearing viewers.

Embedded media and iframes

<embed> requires height, src, type, and width to be specified explicitly; a missing type leaves the browser guessing the embedded content’s format from the response, which is unreliable.

<iframe>:

  • MUST specify explicit width and height (or be sized via CSS), for the same layout-stability reason as <img> — see The img element.
  • SHOULD set scrolling="no" where the embedded content is not meant to scroll independently of the page, since some browsers (historically Firefox) render a visible scrollbar by default that others do not.
  • MUST use id, not name, for referencing the iframe from the parent document’s own scripts or styles, consistent with Coding style; name remains relevant only where the iframe is itself the target of a form submission or a link’s target attribute.
  • Embedding third-party content (an ad, a widget, a payment form) MUST use the sandbox attribute, scoped to only the permissions that specific embed genuinely needs (allow-scripts, allow-forms, and similar), rather than left unset — an unsandboxed third-party iframe runs with the same privileges as the embedding page.

On iOS, an <iframe> with scrolling="no" has historically not prevented the embedded document’s own content from scrolling if that content overflows its frame — the fix is on the embedded document’s own side, constraining its content’s height or setting overflow: hidden on its own root element, not something the embedding page’s iframe attribute alone can control.

Scripting and templates

Classes and data attributes

class and data-* MUST be used for strictly separate purposes:

  • class is exclusively a CSS styling hook, per Coding style. It MUST NOT be queried by JavaScript to select or identify elements — a class renamed during a CSS refactor should never silently break application behavior.
  • data-* is exclusively the mechanism JavaScript uses to read parameters and select elements from the DOM. A script-added class (used, for example, to trigger a CSS transition from JavaScript) SHOULD be prefixed is- or has- (is-open, has-error) to visually distinguish a script-controlled state class from an ordinary styling class in the stylesheet.

The type attribute on <script> MAY be omitted for a standard JavaScript module or classic script — the default is correct in either case for a current browser; set it explicitly only for a non-executable use such as type="text/template" (see Templates) or type="module" where module semantics (deferred execution, import/export) are specifically needed.

Inline style attributes MUST NOT be used for static presentation — that is what a stylesheet is for. The one accepted exception is a value that is genuinely computed at runtime by JavaScript, such as the live position of an element during a drag-and-drop interaction, where the value has no meaningful static CSS representation.

Progressive enhancement

A meaningful share of real-world traffic — historically estimated around 5% — either has JavaScript disabled, fails to load it, or runs a browser whose JavaScript engine cannot execute a modern bundle at all. A page’s core content MUST render as static HTML that does not depend on JavaScript having run, and interactive enhancements MUST be layered on top of that baseline, not required for it.

  • A component that genuinely cannot function without JavaScript (a rich interactive widget with no static equivalent) SHOULD be rendered by JavaScript at runtime, rather than emitted as broken or non-functional markup by the server and then "fixed" once a script runs — a user without JavaScript then sees nothing, rather than seeing something that looks interactive but does not work.
  • Prefer a CSS animation or transition over a JavaScript-driven one wherever the effect is achievable in CSS — see SVG animation for the same preference applied to SVG specifically.
  • Bundled and minified JavaScript SHOULD be served with a long cache lifetime, the same as static images — see Serving images — since a content-hashed filename invalidates the cache automatically on change.
  • defer (for scripts that can run after parsing completes, in source order) and async (for independent scripts that may run as soon as they are fetched, in arbitrary order) SHOULD be used in preference to the legacy pattern of placing an unattributed <script> at the very bottom of <body> to avoid blocking rendering — defer/async express the actual execution requirement directly, rather than relying on source position as a proxy for it.
  • Where the difference between "no JavaScript at all" and "JavaScript ran but failed, or the browser’s API surface is incomplete" matters, <noscript> alone only covers the first case. A short, feature-tested script placed early and run synchronously — checking for the specific APIs the page depends on (addEventListener, querySelector, and similar; this is the "cut the mustard" pattern) — is what should gate enhancement for the second case, showing an in-place message or falling back to the static baseline where the test fails.
  • No inline <script> block containing application logic SHOULD appear in the page body — application JavaScript belongs in an external, cached, minified file. A short, synchronous script used purely to apply an early class or feature-test result before first paint is the accepted exception.
  • Event handlers MUST be attached with addEventListener, never with an inline HTML event attribute (onclick, onload, and similar) — see JavaScript-driven links for the same rule applied to links specifically.

Data attributes

data- attributes are the *exclusive channel through which server-rendered HTML passes parameters to client-side JavaScript.

  • JavaScript MUST NOT select elements by class or id as its primary query mechanism for behavior — data-* attributes (commonly paired with [data-component="…​"]-style selectors) decouple styling hooks from behavior hooks, so a designer changing a class name does not silently break a script.
  • No parameter needed by a script MUST be passed via an inline <script> block embedding values directly in the page — encode it as a data-* attribute on the relevant element instead, which keeps the parameter next to the markup it describes and inspectable in the DOM.
  • Server-rendered content is what kicks off client-side behavior — a script reads the already-rendered markup and its data-* attributes to initialize, rather than the server rendering an empty placeholder for JavaScript to fill entirely. This is the same principle as Progressive enhancement applied at the level of a single component.
  • Every enhancement MUST fail silently into the working static baseline if it errors — an uncaught JavaScript error in one component’s enhancement script MUST NOT break the page’s core content or any other component’s behavior.

Templates

<template> declares inert, non-rendered markup that can be cloned and injected into the document at runtime via JavaScript — the standard mechanism for a client-side reusable markup fragment. Its content is parsed but not rendered, not executed (scripts inside it do not run until cloned in), and not reachable by DOM queries against the live document until explicitly cloned.

<template> is now widely supported and SHOULD be used for this purpose in new work. The older interim pattern of storing a template inside a <script type="text/template"> block (relying on the browser not executing an unrecognized script type, and reading its textContent) predates broad <template> support and SHOULD NOT be used in new code — it is mentioned here only because it may still be encountered in legacy code.

Built-in widgets

The web platform provides a small number of built-in interactive elements beyond form controls. <meter> (a scalar value within a known range — disk usage, a rating) and <progress> (the completion progress of a task) are both excluded from this standard’s Allowed elements list, on the grounds of inconsistent visual rendering across browsers for a working-standard subset that favors elements a team can style predictably; a custom component built from <div>`s with the equivalent ARIA role (see Other live-region components) gives more consistent control. A project MAY choose to permit `<meter>/<progress> as an extension to this standard where their native semantics and lower implementation cost outweigh that inconsistency for its own use case.

<menu> and its associated context-menu behavior are similarly excluded — see Allowed elements — as unsupported/inconsistent enough across current browsers to not be relied upon.

Content Security Policy

A Content Security Policy (CSP) restricts which sources a document may load scripts, styles, images, and other resources from, and is a primary defense against cross-site scripting. A policy SHOULD be set via the Content-Security-Policy HTTP response header where the server controls it, with the <meta http-equiv="Content-Security-Policy"> tag (see Meta tags) as the fallback where it does not. See TS-52: Security and secrets management for the security requirements a CSP forms part of; this standard covers only where and how its HTML-level declaration is placed.

Semantics and metadata

Semantic markup

HTML’s job is to describe what content is, not how it looks. Semantic markup — choosing the element whose meaning actually matches the content, <nav> over a <div> styled to look like navigation — matters because the same markup then delivers that meaning correctly to every consumer, regardless of how they access the page: a sighted user with CSS, a screen reader, a search engine crawler, a browser extension, or a future tool that does not exist yet. A <div> styled to look exactly like a button conveys nothing to any of those beyond the sighted-with-CSS case.

Two disciplines follow from this:

  • Use elements only within the scope their specification defines them for — do not repurpose an element because its default styling happens to be convenient (a <blockquote> used purely to get an indent, with no actual quotation).
  • Do not reinvent markup the platform already provides. A custom dropdown built entirely from <div>`s duplicates work `<select> already does, and starts from zero on every accessibility behavior <select> gets for free.

Presentational tags

The following elements describe presentation directly rather than meaning, and MUST NOT be used: big, blink, center, font, marquee, strike, tt, nobr. Each is either formally deprecated or was never standardized, and each has a semantic or CSS-based replacement.

<b>, <i>, <u>, and <small> warrant a specific note, because whether they are presentational depends on how they are used. <u> is presentational in essentially all modern usage (it is easily confused with a hyperlink) and is excluded from this standard’s Allowed elements list entirely. <b>, <i>, and <small> are not excluded — each has a genuine semantic meaning distinct from <strong>/<em> and is permitted under that meaning; see Inline text elements for exactly what that meaning is in each case. Using any of the three purely to make text bold, italic, or smaller, with no semantic justification, remains presentational misuse regardless of the element being technically permitted — reach for CSS instead.

Presentational attributes

border, align, valign, and clear are presentational HTML attributes with direct CSS equivalents, and MUST NOT be used — a border attribute on <table> is CSS border; align/valign are CSS text-align/vertical-align; clear is CSS clear. The style attribute itself follows the same rule set out in Classes and data attributes: reserved for genuinely runtime-computed values, not static presentation.

Redundancy between HTML, ARIA, and metadata

More than one mechanism sometimes expresses the same semantic — <nav>, role="navigation", and an RDFa typeof="SiteNavigationElement" (see Schema.org) all mark navigation, and a document that used all three for the same element would be marking it up three times over for the same one fact.

The rule this standard applies is: prefer native HTML semantics first; add an ARIA role only where HTML has no native element or attribute that already expresses the same meaning; add a metadata vocabulary (Schema.org, Microformats) only for the machine-readable, structured-data facts that neither HTML nor ARIA express at all (a product’s price, a recipe’s cook time). Do not stack a redundant ARIA role onto an element whose tag name already implies that role — see Redundant ARIA for the specific mappings this produces.

Browser support policy

This standard’s Allowed elements list, and the individual exclusions called out throughout (<dialog>, <meter>/<progress> as noted in Built-in widgets, SMIL animation, image maps as the recommended default), follow one consistent policy: an element or feature is excluded, or only conditionally permitted, where its rendering or behavior is not consistent enough across this project’s target browsers to be relied upon without a fallback. This is a moving target — a feature excluded here today may earn broad enough support to be added later, and a project extending this standard for its own target-browser matrix SHOULD revisit each exclusion against that matrix rather than assume the list is permanent.

Schema.org

Public pages MAY be extended with Schema.org structured data, expressed via RDFa Lite, which this standard prefers over the Microdata serialization — RDFa Lite is more actively maintained as Schema.org’s own recommended syntax and avoids Microdata’s need to repeat itemscope on every nested object. Do not mix the two syntaxes on the same page.

RDFa Lite uses five attributes:

vocab

Declares the vocabulary in use, set once on <body>vocab="https://schema.org/".

typeof

Declares an element’s Schema.org type — typeof="Product", typeof="Article".

property

Declares which Schema.org property an element’s content maps to — property="name", property="price".

resource

Points a property at another resource by URI, where the value is a reference rather than inline content.

prefix

Declares an additional vocabulary prefix beyond the default, where more than one vocabulary is in use.

Markup SHOULD be validated against Google’s or Schema.org’s own structured-data validator before shipping, since a subtly malformed RDFa attribute fails silently — the page still renders normally, but the structured data is not picked up. Do not duplicate a Schema.org typeof/property pair for information already conveyed by a native HTML sectioning element — <main>, <nav>, <header>, <aside> — where an equivalent Schema.org type exists (see Redundancy between HTML, ARIA, and metadata).

Microformats

Microformats are a small set of rel values conveying link-relationship semantics, distinct from Schema.org’s structured-object semantics. The ones in regular production use:

rel="nofollow"

Signals a link the page does not want to vouch for or pass ranking signal to — a link in user-submitted comments, a paid or sponsored link (paired with rel="sponsored"), or any link the page does not editorially endorse.

rel="license"

Points to the license governing the linked-to content.

rel="tag"

Marks a link as a tag/category label for the current page.

Prefer Schema.org for structural, object-shaped information (a product, an article, a recipe) and Microformats only for these link-relationship semantics — the two are not interchangeable, and Schema.org has no equivalent for "this specific link is unendorsed."

Social graphs

The Open Graph protocol (<meta property="og:*">) controls how a page renders when shared on Facebook and several other platforms that adopted the same tags — title, description, and preview image, independent of the page’s own <title>/description meta values. Adopt it where social sharing is a meaningful traffic channel for the product; it is not REQUIRED for every page, only for those a team expects to be shared.

Internationalization

The root <html> element MUST declare both lang and dir, always paired: <html lang="en" dir="ltr">. lang values SHOULD follow RFC 4646 — an ISO 639-1 language code, optionally combined with an ISO 3166-1 alpha-2 region (en-GB, en-US) where the distinction is meaningful to the content (regional spelling, date formats, currency). Prefer the culture-specific locale form (en-GB) over the bare language code where the distinction genuinely matters to the content’s audience.

dir sets the base text direction (ltr or rtl) for the whole document, and is not purely presentational — several CSS layout properties (margin-inline-start, and similar logical properties) key off it, so dir functions as a real layout toggle, not just a text-rendering hint.

Two elements handle direction at a finer grain than the document root:

<bdo>

Explicitly overrides the bidirectional text algorithm’s default direction for its content — for a short run of text that must render in the opposite direction from its surrounding context regardless of what the algorithm would otherwise infer.

<bdi>

Isolates a run of text whose direction is not known in advance (typically user-generated content — a name, a username) so its direction does not leak into and disrupt the surrounding text’s layout. dir="auto" on the element itself is the fallback where the content’s own direction should be detected automatically rather than isolated.

Accessibility

Keyboard navigation

Keyboard operability is the cornerstone of accessible interaction: every interactive element MUST be fully operable from the keyboard alone, with no functionality that depends on a mouse, touch, or hover.

  • Native interactive elements (<a href>, <button>, form controls) are keyboard-operable by default — this is one of the strongest reasons to prefer them over a custom-built equivalent, per Semantic markup.
  • tabindex="0" adds a non-natively-focusable element to the natural tab order, at its position in the DOM. tabindex="-1" removes an element from the tab order while still allowing it to receive focus programmatically (via .focus()) — used for a container that needs to receive focus on open (a modal, per Modals and focus management) without itself being tab-reachable.
  • A positive tabindex (tabindex="1", tabindex="2", and so on) overrides source order with an explicit numeric order, and SHOULD be avoided — it is difficult to maintain consistently as a page evolves, and a page relying on it usually indicates the DOM order itself should be fixed instead. Where a positive tabindex is genuinely unavoidable, use large, widely spaced increments (100, 200, 300) so a later insertion has room to fit in sequence without renumbering everything after it.
  • Every custom-built interactive component (see Custom input controls, Custom buttons) MUST be fully operable by keyboard, replicating the keyboard behavior a native equivalent would have had.

Modals and focus management

Opening a modal dialog MUST move focus into the modal — typically to its first focusable element or its heading — and background navigation MUST be disabled for the duration: focus MUST be trapped within the modal’s content (Tab and Shift+Tab cycle only among the modal’s own focusable elements) rather than escaping to page content the user cannot currently see or reach. The modal MUST be closable via the Escape key as well as an explicit close control, and closing it MUST return focus to the element that triggered it, so a keyboard user’s position in the page is preserved across the interaction.

Accesskey

accesskey binds a keyboard shortcut directly to an element, but is a suggestion the browser is free to override, commonly collides with the operating system’s or the assistive technology’s own shortcuts, and has no good answer for internationalization (a shortcut letter chosen for an English word may be meaningless or already taken in another locale). accesskey SHOULD NOT be used; where a project wants keyboard shortcuts, implement them with JavaScript keydown listeners instead, and surface the available shortcuts to the user through the interface rather than relying on `accesskey’s own (poorly supported) advertisement mechanism.

Visibility

The hidden attribute (see Coding style) removes an element from both rendering and the accessibility tree — appropriate for content that is "not currently relevant," full stop, for every consumer alike.

Hiding content from some clients but not others — the common case of a tabbed interface, where every panel’s markup exists but only one is visually shown at a time — needs case-by-case judgment rather than one universal technique:

  • Content hidden only visually, but still meant to be discoverable by a screen reader (a "skip to content" link, for example), MUST NOT use display: none or visibility: hidden — both remove the content from the accessibility tree as well as the visual rendering. The standard screen-reader-only technique instead clips the element to a 1px box, positions it absolutely, and hides overflow, while leaving it in normal document flow and unaffected by display/visibility.
  • An inactive tab panel, conversely, generally SHOULD be hidden from both sighted and assistive-technology users simultaneously (with hidden or an equivalent), since its content genuinely is not part of the current view for anyone — see Tabs and accordions for the ARIA states that accompany this.
  • Utility classes for "visible only in print" or "announced only by a screen reader" content are a reasonable pattern for expressing these distinct visibility intents consistently across a codebase.

WAI-ARIA

WAI-ARIA supplies roles, states, and properties for conveying semantics that HTML alone does not express — required for custom, JavaScript-driven widgets (tabs, trees, tooltips, custom dialogs) that have no native HTML equivalent. Prefer a native HTML element wherever one exists — see Semantic markup — and reach for ARIA only for the gap that remains. Some ARIA usage that was once necessary has since been superseded by native HTML additions (sectioning elements superseding several landmark roles, for example — see Landmark roles); where a native alternative now exists, prefer it over the ARIA role that used to be the only option.

Redundant ARIA

An ARIA role or state MUST NOT be added where the element’s native semantics already convey the exact same thing — the redundant declaration adds no information and is one more place for markup to drift out of sync with reality as the code evolves:

Redundant

Because

<main role="main">

<main> already implies the main landmark role.

<nav role="navigation">

<nav> already implies the navigation landmark role.

<button role="button">

<button> already implies the button role.

<input required> aria-required="true"

The native required attribute already conveys this.

<…​ hidden> aria-hidden="true"

The native hidden attribute already removes the element from the accessibility tree.

role="presentation" (removing an element’s default semantics rather than adding to them) is the inverse case, and has its own caveat: applying it to an element that has focusable descendants can strip semantics those descendants still need, so it MUST only be applied where the whole subtree is genuinely presentation-only.

ARIA roles

The roles below cover the components most commonly built without a native HTML equivalent. Each is introduced in the section covering the component it applies to, rather than repeated here as a flat list: alert/alertdialog/dialog (Alerts and dialogs), tooltip (Tooltips), button/toolbar (Custom buttons), checkbox/radio/radiogroup/textbox/combobox/listbox/spinbutton/slider (Custom input controls), grid/gridcell/row/columnheader (Grids), tab/tablist/tabpanel (Tabs and accordions), tree/treeitem/treegrid (Trees), menu/menubar/menuitem/menuitemcheckbox/menuitemradio (Menus), search (Search), progressbar/scrollbar/status/timer/marquee/log (Other live-region components), and the landmark roles (Landmark roles).

ARIA states and properties

Beyond roles, ARIA defines aria-* attributes for states and properties that change dynamically as the user interacts with a component — whether something is expanded, checked, selected, or invalid. Every ARIA state that a component’s interaction changes at runtime MUST be updated by the same script that changes the visible state, so the two never drift apart — a visually expanded panel with aria-expanded="false" still attached is worse than not having the attribute at all, because it actively reports the wrong state.

Live regions

aria-live marks a region whose content changes are announced to assistive technology as they happen, without the user having to move focus to notice them — a form validation summary, a "message sent" confirmation, a live-updating status count.

  • aria-live="polite" announces the update after the screen reader finishes whatever it is currently reading — the correct default for most updates.
  • aria-live="assertive" interrupts immediately, and SHOULD be reserved for genuinely time-critical updates (an error that blocks progress) — overuse of assertive is disruptive in the same way an unexpected interruption is disruptive to a sighted user.

Applications versus documents

role="application" tells assistive technology to hand off most of its own keyboard shortcut handling to the page’s own JavaScript, and role="document" is its inverse, restoring standard document reading behavior within a region that would otherwise inherit application. Both are last-resort tools: role="application" in particular disables assistive-technology behavior users rely on throughout the rest of the page, and SHOULD only be applied to a component that is genuinely a full interactive application in miniature (an embedded spreadsheet or code editor), not to an ordinary interactive widget that the specific roles elsewhere in this section already cover.

Presentational and separator roles

role="presentation" is covered in Redundant ARIA above. role="separator" marks a purely visual divider that also carries semantic meaning as a boundary (distinct from `<hr>’s thematic-break meaning within content — see Horizontal rules) — for example, a divider between groups of items in a toolbar or menu, where the divider itself is not interactive.

Labelling and describing

Three mechanisms attach an accessible name or description to an element, each suited to a different case:

aria-labelledby

References the id of one or more other elements whose text content becomes the accessible name. Preferred where the label text already exists visibly elsewhere on the page — referencing it avoids duplicating the string.

aria-describedby

References the id of one or more elements providing a longer supplementary description, distinct from the name — a form field’s associated hint text, for example. Unlike aria-labelledby, a single aria-describedby target is commonly reused by more than one element.

aria-label

Sets an accessible name directly as a string, with no visible on-page counterpart. Use it only where no visible label exists to reference — an icon-only button with no visible text, for example — since a programmatically supplied label with no visible equivalent cannot be proofread or updated by anyone not editing the markup directly.

Tooltips

There is no native HTML tooltip element. A custom tooltip MUST use role="tooltip" paired with aria-describedby on the element it describes, and MUST be reachable and dismissible by keyboard: it MUST appear on focus (not only on hover) and disappear on blur, and MUST be dismissible with Escape without moving focus away from the element it is attached to.

Alerts and dialogs

role="alert" announces a message immediately and assertively, without requiring focus to move to it — appropriate for a brief, self-contained notification. role="alertdialog" and role="dialog" are both used for a modal that requires user interaction to dismiss; alertdialog for one communicating an urgent message the user must acknowledge, dialog for a general-purpose modal. Either kind MUST move focus into the dialog on open and return it to the triggering element on close, per Modals and focus management.

Custom buttons

Where a custom-built button-like control cannot use <button> directly (rare, but occurring inside some composite widgets), it MUST carry role="button", respond to both the Spacebar and Enter keys as activation triggers, and be in the tab order (tabindex="0"). A toggle button additionally exposes its pressed state via aria-pressed; a button that controls a separate, identifiable region of content exposes that relationship via aria-controls. A group of related buttons (a toolbar of formatting controls, for example) SHOULD be wrapped in a container with role="toolbar".

Forms accessibility

Beyond the native <label>/for association covered in Labels, fieldsets, and legends:

  • aria-invalid="true" MUST be set on a field currently failing validation, so assistive technology announces the invalid state at the point of the field, not only in a summary elsewhere on the page.
  • aria-required="true" is needed only where a project has implemented its own required-field validation instead of the native required attribute (per Validation and input attributes's preference for custom validation) — where required is used directly, it already conveys this and aria-required would be redundant, per Redundant ARIA.
  • aria-describedby associates a field with its own hint or error text, the same mechanism as Labelling and describing applied to a form context.
  • aria-labelledby covers a custom label construction that is not a simple <label for> pairing — a composite label built from more than one text node, for example.

role="search" marks a region of the page as a search feature — a search form plus its associated controls — for a component more complex than a single <input type="search">, giving assistive technology a landmark to jump directly to it.

Custom input controls

A custom-built input control (built where no native HTML control fits, or where a project deliberately restyles beyond what native controls allow) MUST replicate both the ARIA role and the keyboard behavior of its native counterpart:

Control

Role

Keyboard

Checkbox

checkbox, with aria-checked

Spacebar toggles.

Radio group

radiogroup containing radio items, with aria-checked

Arrow keys move selection within the group; only the selected item is in the tab order.

Text box

textbox

Standard text-editing keys; no special handling beyond native input behavior.

Combobox

combobox, with aria-expanded and aria-autocomplete

Down arrow opens the list; arrow keys move through options; Enter selects; Escape closes.

Listbox

listbox containing option items, with aria-activedescendant

Arrow keys move the active option; Enter or Space selects it.

Spin button

spinbutton

Up/Down arrows increment/decrement the value.

Slider

slider

Arrow keys adjust the value; Home/End jump to the min/max.

aria-readonly marks a control that is visible and focusable but not editable, distinct from disabled, which removes it from the tab order entirely.

Drag-and-drop

An accessible drag-and-drop interaction MUST provide a keyboard-operable equivalent — dragging is inherently a pointer-only gesture, so a keyboard-only or switch-device user needs an alternative path to the same outcome (commonly, a "grab" action on Enter/Space, arrow keys to move the grabbed item, and Enter/Space again to drop). aria-grabbed marks an item currently picked up, and aria-dropeffect marks a valid drop target, on the elements participating in the interaction. Where the interaction is naturally a reordering within a list or grid, the relevant grid roles (see Grids) combined with keyboard reordering are often a more robust pattern than raw drag-and-drop roles.

Grids

The grid, gridcell, row, and columnheader roles describe a table-like interactive structure with cell-level keyboard navigation — a spreadsheet-style component, for example — and are unrelated to CSS Grid, which is a purely visual layout mechanism with no bearing on ARIA semantics. Reach for the grid roles only when building an interactive, cell-navigable widget; an ordinary data table uses <table> directly, per Tables.

Tabs and accordions

A tabbed interface uses tablist (the container), tab (each clickable tab), and tabpanel (each content panel) roles. aria-selected marks the currently active tab; only the active panel’s content is visible at once, per Visibility. An accordion is the same pattern rendered vertically, with aria-expanded on each header controlling its own panel rather than one panel at a time being exclusive.

Trees

tree, paired with treeitem for each node, describes a hierarchical navigation structure — a file/directory browser, a nested category list. treegrid extends the same pattern where each row additionally has multiple columns of data, combining tree semantics with the grid roles above.

menu, menubar, menuitem, menuitemcheckbox, and menuitemradio describe an application-style menu system (a desktop-application menu bar, replicated in a web app). For a website’s ordinary navigation menu, prefer a plain <ul> of links inside <nav> instead — see Source order and document structure — which is both simpler and better understood by assistive technology than the menu role family, which was designed for application menus, not site navigation.

Other live-region components

progressbar, scrollbar, status, timer, marquee, and log cover further specialized live-updating components — a progress indicator, a custom scrollbar, a general status message region, a countdown, a scrolling ticker, and an append-only log respectively. Each is a narrower, more specific variant of the live-region pattern introduced in Live regions above, for the specific kind of update it names.

Landmark roles

Most ARIA landmark roles are now superseded by the HTML sectioning elements in Source order and document structure<nav>, <main>, <aside>, and <footer> already imply the navigation, main, complementary, and contentinfo roles respectively, so adding the role explicitly is redundant, per Redundant ARIA. role="banner" and role="contentinfo" remain useful specifically where a project is not yet in a position to use <header>/<footer> at the page’s outermost level, or where more than one <header>/<footer> exists and only one is the page’s banner or contentinfo, which needs distinguishing. role="heading" SHOULD NOT be used — a heading is always better expressed with a native <h1><h3> element, per Headings.

Best practices

  • role="navigation" (or <nav>) MUST NOT be nested inside another navigation landmark — nest a <nav> for a sub-menu inside the outer `<nav>’s content without repeating the landmark role.
  • role="navigation" MUST NOT be applied to an individual link — a landmark is a region, not a single interactive element.
  • A navigation landmark SHOULD contain more than one link — a region wrapping a single link gains nothing from being marked as a navigation landmark.
  • An element MUST NOT be labelled twice by conflicting mechanisms (a visible <label> plus a contradictory aria-label) — where both are present, aria-label silently overrides the visible label for assistive technology, which then announces something different from what a sighted user reads.
  • A document SHOULD have exactly one main landmark (one <main> element).

Keyboard interaction patterns

Beyond the per-control patterns already given in Custom input controls, two composite widgets have their own conventional keyboard patterns:

  • Tabs — Left/Right arrow (or Up/Down, for a vertically oriented tablist) moves between tabs; Home/End jump to the first/last tab.
  • Trees — Down/Up arrow moves between visible nodes; Right expands a collapsed node or moves into its first child if already expanded; Left collapses an expanded node or moves to its parent if already collapsed; Enter activates the focused node; Home/End jump to the first/last visible node.

Testing

Accessibility MUST be verified by more than automated tooling alone — automated checks catch missing attributes and invalid ARIA but cannot verify that a keyboard interaction actually behaves correctly or that a screen reader announces something sensibly:

  • Keyboard-only testing. Unplug the mouse and operate the whole flow being tested using only Tab, Shift+Tab, Enter, Space, and arrow keys.
  • One-handed / adverse-conditions mobile testing. Test touch interactions one-handed and, where practical, in a genuinely distracting environment (walking, in bright sunlight) — conditions a meaningful share of real usage happens under.
  • An OS screen reader. Test with at least one platform screen reader (VoiceOver, NVDA, or JAWS) actually enabled, not only against automated ARIA-validity checks.
  • Real users with disabilities, where feasible, remain the most reliable check of all — automated and expert testing both approximate what actual usage reveals.

Accessibility checklist

The checklist below is organized under WCAG 2.2’s four core principles — Perceivable, Operable, Understandable, and Robust — as a concrete, HTML-level companion to the guidance above, targeting WCAG 2.2 Level AA. It restates specific testable requirements; it does not repeat the rationale already given in the sections above for each.

Perceivable — text alternatives

  • Every <img> has appropriate alt text — see The img element — distinguishing informative images (a description of content) from decorative ones (alt="").
  • Every <button> and icon-only control has an accessible name (visible text, or aria-label per Labelling and describing).
  • Every form input has an associated label — see Labels, fieldsets, and legends.
  • Non-text media (audio, video) has a text equivalent — see Perceivable — time-based media below.
  • Every <iframe> has a title attribute describing its content.

Perceivable — time-based media

  • Pre-recorded audio and video content has a transcript.
  • Video with spoken content has synchronized captions — see Audio and video's <track kind="subtitles"> requirement.
  • Video conveying meaning visually that is not in the audio track (an on-screen action with no narration) has an audio description track or equivalent.

Perceivable — adaptable

  • Source order is logical when read linearly, independent of CSS — see Source order and document structure.
  • Semantic markup is used for headings, landmarks, and lists rather than purely visual styling.
  • Sectioning elements or ARIA landmarks divide the page into identifiable regions.
  • Tables are used only for genuinely tabular data — see Tables.
  • <fieldset>/<legend> groups related form fields — see Labels, fieldsets, and legends.
  • Typed inputs carry an appropriate autocomplete value where relevant — see Validation and input attributes.
  • No instruction relies solely on shape, size, visual position, or sound ("click the round button", "the beep means submit") to be understood.
  • Content is not locked to a single orientation unless that orientation is essential to its function.

Perceivable — distinguishable

  • Color is never the sole means of conveying information (a form error shown only as a red border, with no icon or text) — pair it with text or an icon.
  • Text contrast meets at least 4.5:1 for normal text and 3:1 for large text and UI components, per WCAG 2.2 Level AA.
  • Content remains usable at 200% browser zoom.
  • Content reflows correctly at a 320px viewport width without requiring horizontal scrolling.
  • Text remains readable when a user overrides line height, paragraph spacing, letter spacing, or word spacing via their own stylesheet (WCAG’s text-spacing criterion).
  • Content revealed on hover or focus (a tooltip, a submenu) can be dismissed without moving the pointer, remains visible while the pointer is over it, and persists until dismissed or no longer relevant.

Operable — keyboard accessible

  • Every interactive element is operable by keyboard alone — see Keyboard navigation.
  • No accesskey conflicts with a common browser or assistive-technology shortcut — see Accesskey.
  • No keyboard trap exists anywhere in the page (a widget the user can Tab into but not back out of).
  • Any custom keyboard shortcut can be turned off, remapped, or is only active while the relevant component has focus.

Operable — enough time

  • Any time limit on completing an action can be turned off, adjusted, or extended by the user, unless the time limit is essential (a live auction, for example).
  • Auto-updating or auto-moving content (a carousel, a ticker) can be paused, stopped, or hidden.

Operable — seizures

  • Nothing on the page flashes more than three times per second.

Operable — navigable

  • A "skip to main content" link is the first focusable element on the page.
  • Every page has a descriptive, unique <title> — see Title.
  • Focus order follows a logical, predictable sequence matching visual layout.
  • Every link’s purpose is clear from its own text or its immediate surrounding context, without requiring the user to guess from destination alone.
  • Two links with identical visible text that go to different destinations are distinguishable by their surrounding context or an aria-label/aria-describedby addition.
  • More than one way exists to locate a given page (navigation menu, search, sitemap) where the site has more than a handful of pages.
  • Headings and labels describe their topic or purpose accurately.
  • The current keyboard focus is always visibly indicated.

Operable — input modalities

  • Every function that can be triggered by a complex or multi-point gesture also has a single-point activation alternative.
  • Click/tap handlers fire on click (or onmouseup/pointer-up equivalents), not on mousedown/pointer-down alone — activating on the down-event gives the user no chance to move away and cancel the action.
  • An element’s accessible name includes any visible text label it has, so voice-control software that targets elements by their visible text can find it.
  • Any function requiring device motion (shaking, tilting) also has a conventional UI control as an alternative.

Understandable — readable

  • The page’s primary language is declared on <html lang> — see Internationalization.
  • Any passage in a different language from the page’s primary language has its own lang attribute.

Understandable — predictable

  • Moving keyboard focus to an element does not, by itself, trigger a context change (a navigation, a form submission) — a context change happens only on an explicit action like a click or an Enter press.
  • Navigation is presented in the same relative order across pages that share it.
  • Components with the same function are identified consistently across the site (the same icon and label for "search" everywhere, for example).

Understandable — input assistance

  • Where an input has a required format, that format is stated in its label or adjacent hint text before the user submits, not only after an error.
  • Validation errors are announced to assistive technology (via aria-invalid and aria-describedby, per Forms accessibility) and identified in text, not only by a color change.
  • Error messages suggest the correction needed, not only that an error occurred.
  • A legal, financial, or otherwise consequential submission (a payment, a data-deletion request) can be reviewed, corrected, or reversed before it takes irreversible effect.

Robust

  • Markup parses without duplicate id values or improperly nested elements — see Coding style.
  • ARIA roles, states, and properties are used according to their specification, matching the tables and patterns given earlier in this section.
  • Status messages (a "saved" confirmation, a loading state) are programmatically determinable via a live region, per Live regions, without requiring a focus change to be noticed.

References