Use when picking which HTML element to author for a region or piece of content, when a screen reader cannot find the navigation or main content, when an audit reports missing landmarks, or when a search field has no role. Prevents the `<div role="navigation">` reflex when `<nav>` exists, `<form role="search">` patterns now superseded by the `<search>` element, multiple `<main>` per page, `<section>` blocks without headings, `<article>` wrappers around unrelated content, and `tabindex` placed on `<dialog>`. Covers the semantic landmark surface (`<header>`, `<nav>`, `<main>`, `<aside>`, `<footer>`, `<article>`, `<section>`, `<search>`), the `<search>` element (Baseline Widely Available since October 2023, implicit role `search`), `<details>` / `<summary>` with the `name` attribute for accordion-style mutual exclusion (Baseline 2024), declarative Shadow DOM via `<template shadowrootmode>`, and the rule for when to use ARIA `role` versus a native element. Keywords: header, nav, main, aside, footer, article, secti
Use when picking which HTML element to author for a region or piece of content, when a screen reader cannot find the navigation or main content, when an audit reports missing landmarks, or when a search field has no role. Prevents the `<div role="navigation">` reflex when `<nav>` exists, `<form role="search">` patterns now superseded by the `<search>` element, multiple `<main>` per page, `<section>` blocks without headings, `<article>` wrappers around unrelated content, and `tabindex` placed on `<dialog>`. Covers the semantic landmark surface (`<header>`, `<nav>`, `<main>`, `<aside>`, `<footer>`, `<article>`, `<section>`, `<search>`), the `<search>` element (Baseline Widely Available since October 2023, implicit role `search`), `<details>` / `<summary>` with the `name` attribute for accordion-style mutual exclusion (Baseline 2024), declarative Shadow DOM via `<template shadowrootmode>`, and the rule for when to use ARIA `role` versus a native element. Keywords: header, nav, main, aside, footer, article, section, search element, dialog, details, summary, hgroup, figure, figcaption, landmark, landmark roles, implicit ARIA role, declarative shadow DOM, shadowrootmode, shadowrootdelegatesfocus, document outline, skip link, screen reader cannot find navigation, no landmarks announced, content does not appear in outline, search field has no role, accordion not exclusive, what HTML element should I use, when do I use article vs section, when do I use section vs div, how to make navigation accessible, what is the search element, do I need ARIA, how to write semantic HTML.
license
MIT
compatibility
Designed for Claude Code. Requires Frontend Design evergreen-2026.
metadata
{"author":"OpenAEC-Foundation","version":"1.0"}
Frontend Syntax HTML5 Semantic
This skill fixes the choice of HTML element for every region and content type. It is built on the architecture position from [[frontend-core-architecture]]: the markup layer is the foundation, and native semantic elements ALWAYS beat <div role="..."> for accessibility tree mapping, focus management, and platform integration.
The skill targets evergreen-2026 browsers. The <search> element (Baseline Widely Available since October 2023) and <details name=""> accordion mutual exclusion (Baseline 2024) are treated as available without polyfill. Declarative Shadow DOM via <template shadowrootmode> is Baseline becoming Widely Available.
Quick Reference
Landmark elements and implicit ARIA roles
Element
Implicit role
Cardinality per page
Purpose
<header>
banner when child of <body>, generic elsewhere
one banner per document
Top-of-page identification (logo, site name, primary navigation slot)
<nav>
navigation
multiple allowed if each is labelled
Block of navigation links
<main>
main
exactly one per document
Primary content unique to the page
<aside>
complementary
multiple allowed
Tangentially related content (call-outs, sidebars)
<footer>
contentinfo when child of <body>, generic elsewhere
one contentinfo per document
Bottom-of-page info (copyright, secondary links)
<article>
article
multiple allowed
Self-contained, syndicatable item (blog post, comment, card)
<section>
region only when given an accessible name
multiple allowed
Thematic grouping with a heading
<search>
search
typically one per page
Container for the controls that PERFORM a search (not the results)
<figure>
figure
multiple allowed
Self-contained content (image, code block, diagram) plus optional caption
<details>
group (with <summary> as button)
multiple allowed
Native disclosure widget
<dialog>
dialog or alertdialog per usage
multiple allowed
Modal or non-modal dialog; full mechanics in [[frontend-impl-popover-dialog-anchor]]
Native elements get correct focus order, correct keyboard interaction, correct accessibility tree mapping, and correct platform integration for free. The full Authoring Practices comparison lives in [[frontend-a11y-aria-patterns]].
Decision Trees
Decision: which landmark element fits this region?
Top-of-page identification (site name, logo, primary navigation slot)
-> <header> (one banner per document, direct child of <body>)
Block of navigation links
-> <nav>
If multiple <nav> on one page, give each an aria-label or aria-labelledby
Primary content, unique to this page
-> <main> (EXACTLY one per document)
Tangentially related content (call-outs, related-posts, ads)
-> <aside>
Bottom-of-page info (copyright, secondary links)
-> <footer> (one contentinfo per document, direct child of <body>)
Self-contained, syndicatable item (a blog post, a comment, a product card)
-> <article>
Thematic grouping that needs a heading
-> <section> with an h2..h6 inside
If no heading is appropriate -> use <div> instead
Search controls (the input + submit, NOT the results listing)
-> <search>
Use ONLY for the form that performs the search.
NEVER wrap the results listing in <search>.
Self-contained content (image, code, diagram) with optional caption
-> <figure> + optional <figcaption>
Disclosure (collapsible region)
-> <details> + <summary>
For accordion mutual-exclusion: give all sibling <details> the same name attribute.
Decision: do I need role="..." ?
Is there a native HTML element with the right semantics?
yes -> use the element. NEVER add a role; it duplicates or conflicts with the implicit role.
no -> continue
Is the requirement a custom interactive widget (e.g. combobox, tablist, tree, treegrid) that no HTML element implements natively?
yes -> use ARIA. See [[frontend-a11y-aria-patterns]] for the WAI APG patterns and required keyboard and state plumbing.
no -> question your requirement; you probably need a native element you have not considered yet.
The First Rule of ARIA: do not use ARIA if a native element with the required semantics exists. The Second Rule of ARIA: do not change native semantics unless absolutely necessary.
Decision: <article> vs <section> vs <div>
Could this content be syndicated, repeated in a feed, or pulled out as a standalone item?
yes -> <article>
no -> continue
Does this region have a heading and represent a thematic grouping of content?
yes -> <section> (with a heading inside, h2..h6)
no -> continue
Is this region purely a styling or scripting wrapper with no semantic meaning?
yes -> <div>
no -> reconsider; one of the above probably applies.
A <section> without a heading is meaningless to assistive technology. ALWAYS pair <section> with a heading element. NEVER use <section> as a generic styling wrapper.
Patterns
Pattern: the landmark skeleton
ALWAYS structure a document with the canonical landmark skeleton:
Note that <search> wraps the controls that perform the search, NOT the results. The implicit role search on <search> replaces the legacy <form role="search"> pattern; do NOT add role="search" to the inner <form>.
The <search> element groups one or more search-related controls (input, button, filters). It has implicit ARIA role search. Baseline Widely Available since October 2023.
Authoring rules:
ALWAYS wrap the search controls in <search>. NEVER wrap the results listing in <search>.
<search> MAY contain a <form> inside it; the inner form does NOT need role="search" because the outer <search> already provides the landmark.
The element is purely semantic. It has NO default visual styling and NO interactive behavior of its own.
Multiple <search> regions on a single page are allowed (e.g. site search and a filter panel); label each with aria-label if more than one is present.
The <details> element is the native disclosure widget. Its first child MUST be <summary>, which becomes the toggle button.
<details><summary>How do I install?</summary><p>Run the install script.</p></details>
Accordion-style mutual exclusion (Baseline 2024) uses the shared name attribute. All <details> with the same name form an exclusive group: opening one closes the others.
The toggle event fires on open and on close. The element exposes the open boolean attribute; toggling it programmatically opens or closes the disclosure.
NEVER rebuild this widget with <button aria-expanded> plumbing when <details> fits the requirement.
<dialog> is the native modal-or-non-modal dialog element. Per MDN: tabindex MUST NEVER be set on <dialog>. Initial focus goes to the first focusable descendant unless autofocus is set on a specific descendant. Full mechanics (showModal(), show(), close(), closedby, ::backdrop, top-layer behavior) belong in [[frontend-impl-popover-dialog-anchor]].
For this skill the essential rules:
NEVER add tabindex to a <dialog>.
ALWAYS use <dialog> for modal patterns; do NOT roll your own <div role="dialog">.
Implicit role is dialog when opened via show(), and effectively dialog when opened via showModal(). Use role="alertdialog" only for interrupt-style dialogs that demand immediate user response.
Pattern: declarative Shadow DOM via <template shadowrootmode>
Declarative Shadow DOM lets the HTML parser attach a shadow root at parse time, with no JavaScript on the critical path. Three attributes control behavior:
Attribute
Values
Effect
shadowrootmode
open or closed
Required. Marks the <template> as a shadow root payload. open allows script access via element.shadowRoot; closed does not.
shadowrootdelegatesfocus
boolean
When the host receives focus, focus is delegated to the first focusable descendant inside the shadow root.
shadowrootclonable
boolean
The shadow root is cloned when the host element is cloned (e.g. by cloneNode(true)).
<my-card><templateshadowrootmode="open"shadowrootdelegatesfocus><style>:host { display: block; padding: 1rem; border: 1px solid currentColor; }
</style><slot></slot></template>
Card content goes here in light DOM.
</my-card>
Declarative Shadow DOM enables server-rendered web components that hydrate without any JavaScript dependency for first paint. The full custom-element registration model lives in [[frontend-impl-web-components]].
Pattern: document outline and heading hierarchy
The HTML5 document outline is derived from heading elements (<h1> through <h6>), NOT from sectioning elements. ALWAYS write headings in document order; NEVER skip levels for visual reasons.
Rules:
ONE <h1> per document (the page title). Authors MAY repeat <h1> per <article> if the article is rendered standalone in feeds, but a single page-level <h1> is the safe default.
ALWAYS place an <h2> (or lower) inside each <section> and <article>. A <section> without a heading does not contribute a region to assistive technology and reads as meaningless.
NEVER skip a level. After <h2> the next sectioning heading is <h3>, never <h4>.
For grouping a heading with a tagline or subtitle, use <hgroup> with one heading plus paragraph children.
Pattern: when an element accepts the implicit role for free
Sectioning elements behave differently based on context:
<header> has role banner ONLY when it is a direct child of <body> (or of an element whose role is main per some implementations). Inside an <article> or <section>, <header> is generic.
<footer> has role contentinfo ONLY when it is a direct child of <body>. Inside an <article>, <footer> is generic.
<section> has role region ONLY when it has an accessible name (aria-label, aria-labelledby, or title). Without a name, <section> is a generic element with no landmark role.
NEVER assume a sectioning element always exposes a landmark. ALWAYS verify with an accessibility tree inspector (Chrome DevTools, Firefox Accessibility Inspector, axe-core).
Cross-references
[[frontend-core-architecture]] for why the markup layer is the foundation and how native semantics map to the accessibility tree.
[[frontend-syntax-html5-form]] for the form-control surface (<input>, <select>, <textarea>, Constraint Validation API).
[[frontend-impl-popover-dialog-anchor]] for full <dialog> and Popover API mechanics, anchor positioning, and the closedby attribute.
[[frontend-impl-web-components]] for custom-element registration that consumes declarative Shadow DOM.
[[frontend-a11y-aria-patterns]] for when a custom widget genuinely requires ARIA.
[[frontend-a11y-focus-keyboard-inert]] for focus management and the inert attribute.
references/methods.md: complete landmark element matrix with implicit ARIA role per context, attribute surface for <details>, <dialog> and <template>, document outline algorithm summary.
references/examples.md: framework-agnostic code examples including a full landmarked HTML page suitable for an axe-core landmarks audit.
references/anti-patterns.md: six anti-patterns with symptom, root cause, and fix.