SuperAPI AI Directory

frontend-syntax-html5-semantic

Frontend & websites

View repository

Updated 10-15 22:18

About this skill

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]]

Verified against MDN: <search> (verified 2026-05-19), MDN: <details> (verified 2026-05-19), MDN: <template> (verified 2026-05-19), and WHATWG HTML Living Standard, Sections (verified 2026-05-19).

The native-first rule

Need Native element Do NOT do
Navigation region <nav> <div role="navigation">
Search controls region <search> <form role="search">
Banner region <header> (direct child of <body>) <div role="banner">
Page contentinfo <footer> (direct child of <body>) <div role="contentinfo">
Main region <main> <div role="main">
Aside / complementary <aside> <div role="complementary">
Disclosure <details> + <summary> <button> + manual aria-expanded plumbing
Modal <dialog> + showModal() <div role="dialog">

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:

<body>
  <header>
    <a href="/" class="logo">Brand</a>
    <nav aria-label="Primary">
      <ul><li><a href="/products">Products</a></li></ul>
    </nav>
    <search>
      <form role="search-form-only-if-needed" action="/search">
        <label>Search <input type="search" name="q"></label>
        <button>Search</button>
      </form>
    </search>
  </header>
  <main>
    <h1>Page title</h1>
  </main>
  <aside aria-label="Related">
  </aside>
  <footer>
  </footer>
</body>

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>.

Pattern: the <search> element

Source: MDN: <search> (verified 2026-05-19).

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.

Pattern: <details> and the accordion via name

Source: MDN: <details> (verified 2026-05-19).

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.

<details name="faq"><summary>Q1</summary>A1</details>
<details name="faq"><summary>Q2</summary>A2</details>
<details name="faq"><summary>Q3</summary>A3</details>

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.

Pattern: <dialog> light reference

Source: MDN: <dialog> (verified 2026-05-19).

<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>

Source: MDN: <template> (verified 2026-05-19).

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>
  <template shadowrootmode="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.

Reference Links

Reference files local to this skill:

  • 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.