Guides expert-level html semantics implementation: html-css and web-development decision frameworks, production-ready patterns, and concrete templates for html semantics workflows.
A skill is a written procedure a bot loads when a job calls for it. This one is a single file, SKILL.md, and the whole file is on this page.
Guides expert-level html semantics implementation: html-css and web-development decision frameworks, production-ready patterns, and concrete templates for html semantics workflows.
How much does it cost?+
Nothing. It ships with Murage, which is open source. Your bots run on the AI plan you already have.
Can I change it?+
Yes. Open it in Settings → Skills to read, edit or duplicate it. Skill Guard scans it again after every edit.
Give your first job to Murage.
Download the app, connect the AI you already pay for, and tell your Chief of Staff what needs doing. Plan on about ten minutes from install to a working team.
Map user-provided HTML or a description of content onto the four HTML5 content categories that control element nesting validity: flow content, phrasing content, sectioning content, and interactive content
Ask whether the content has meaning independent of its surrounding context (use a sectioning element) or only derives meaning from context (use a grouping or text-level element)
Identify whether the content is the main subject of the page, a related aside, or structural chrome -- this determines the landmark region
Do NOT start with layout needs. A sidebar that is visually on the right is not automatically an <aside> -- it is an <aside> only if the content is tangentially related to the main content
2. Map Landmark Regions
Every page must have a clear, non-overlapping set of landmark regions. These are the top-level semantic scaffold.
Use exactly ONE <main> per page -- it wraps the primary content, never navigation or footers
Use <header> for the site-wide banner at the top; it can also appear inside <article>, <section>, <aside>, or <nav> as a section-level header
Use <footer> for copyright, contact links, legal notices -- also nestable inside sectioning elements
Use <nav> for ALL primary and secondary navigation -- label multiple <nav> elements with aria-label (e.g., aria-label="Primary", aria-label="Breadcrumb")
Use <aside> for complementary content: related links sidebars, pull quotes, author bio boxes, advertisements -- content that makes sense even when separated from the main flow
If a page has more than one <header>, <footer>, or <nav>, every instance except the main site-level one must have a unique aria-label or aria-labelledby
Validate landmark coverage with the axe DevTools browser extension or Deque's accessibility checker -- all content should be reachable from a landmark
3. Build the Document Outline with Heading Hierarchy
The heading structure must function as a table of contents that makes sense read in sequence.
<h1> represents the page title -- use exactly ONE per page (two in single-page applications where a modal has its own <h1> is acceptable but must be managed with aria-modal)
Never skip heading levels: do not jump from <h2> to <h4>. If a heading looks too large, change the CSS, not the heading level
Each sectioning element (<article>, <section>, <aside>, <nav>) should begin with a heading that labels it -- if you do not want a visible heading, use .visually-hidden CSS class (position: absolute; clip: rect(0,0,0,0); not display:none which removes it from the accessibility tree)
<section> requires an accessible name to be meaningful -- apply aria-labelledby pointing to its heading id, or aria-label if no heading is present
Use a heading map tool (HeadingMap browser extension, or document.querySelectorAll('h1,h2,h3,h4,h5,h6') in console) to audit the outline before shipping
Decorative headings used purely for visual chunking that have no document outline meaning should be styled <div> or <p> elements, not headings
4. Select Sectioning and Grouping Elements
Match each content block to its correct semantic container using these decision rules:
<article>: Self-contained, independently distributable content. Blog posts, news articles, product cards, comments, social media posts, forum threads. Ask: "Could this be syndicated via RSS and make sense on its own?" If yes -- <article>.
<section>: A thematic grouping within a larger document. A chapter, a major tab panel content area, a homepage zone (hero, features, testimonials). <section> implies the content is part of the surrounding whole. If you cannot name it, it should probably be a <div>.
<div>: A non-semantic grouping element for styling and scripting hooks. Use it when no semantic element fits. Prefer a semantic element every time one exists. A <div> with a role attribute is almost always inferior to using the correct semantic element.
<p>: A paragraph -- prose content. Do NOT wrap block-level elements inside <p> (this is a parse error). Do NOT use <p> as a spacing tool.
<figure> + <figcaption>: Any content that is referenced from the main text and could be moved to an appendix -- images with captions, code examples, charts, diagrams, embedded videos. <figcaption> must be the first or last child of <figure>.
<details> + <summary>: Native disclosure widget. Use for FAQs, expandable sections. No JavaScript required for show/hide. The <summary> is the visible label when collapsed.
<blockquote> + <cite>: Extended quotations from external sources. cite attribute on <blockquote> takes a URL; <cite> element wraps the title of the work. Do NOT use <blockquote> for visual indentation.
5. Apply Text-Level Semantics Correctly
Text-level elements carry meaning that screen readers and search engines interpret:
<strong>: Strong importance, seriousness, or urgency. Screen readers may announce with emphasis. Do NOT use for general bold styling -- use CSS font-weight on a <span> instead.
<em>: Stress emphasis that changes meaning ("I never said that"). Different from <strong>. Do NOT use for italic styling -- use CSS font-style on a <span>.
<b>: Stylistically offset text without extra importance -- keywords in a document, product names in a review. A semantic compromise when neither <strong> nor a descriptive element fits.
<i>: Alternative voice or mood -- technical terms, foreign phrases, thoughts in narrative. Not just "italic text".
<mark>: Highlighted text for reference purposes -- search result matches, code annotations. Adds role="mark" accessibility semantics in most browsers.
**<abbr title="...">: Abbreviations and acronyms. The title attribute provides the expansion. Use on first occurrence in a document.
<time datetime="YYYY-MM-DD">: Machine-readable dates and times. The datetime attribute must use ISO 8601 format. Enables calendar integration and search engine temporal understanding.
<code>: Inline code. Wrap with <pre> for block code. <kbd> for user keyboard input. <samp> for sample output. <var> for variables.
<del> and <ins>: Content that has been removed or added. Use datetime and cite attributes. Screen readers announce these as deleted/inserted.
NEVER use <br> for spacing between paragraphs -- use CSS margin. <br> is valid only for line breaks that are part of content (poetry, addresses).
6. Mark Up Interactive Components with ARIA Patterns
When native HTML elements cannot express the required widget semantics, augment with WAI-ARIA:
Follow the ARIA Authoring Practices Guide (APG) pattern for each widget type. The APG defines keyboard interaction, required roles, states, and properties.
Tabs: role="tablist" on container, role="tab" on each tab, role="tabpanel" on each panel. Selected tab has aria-selected="true". Panels have aria-labelledby pointing to their tab.
Accordions: Use <button> for the trigger (not <div> or <a>). aria-expanded="true|false" on the button. aria-controls pointing to the panel id.
Modals/Dialogs: role="dialog" with aria-modal="true" and aria-labelledby pointing to the dialog title. Focus must be trapped within the dialog while open. Return focus to the trigger on close.
Alerts/Status messages: role="alert" for urgent messages (announces immediately). role="status" for non-urgent updates (polite announcement). Both are live regions.
The ARIA hierarchy rule: 1st -- use native HTML element; 2nd -- use native HTML with modified semantics; 3rd -- use ARIA role. Never add ARIA that conflicts with native semantics (e.g., <button role="link"> is incorrect).
Always test ARIA patterns with at least two screen readers: NVDA + Firefox on Windows, and VoiceOver + Safari on macOS/iOS.
7. Validate and Audit
Semantic HTML must be verified against multiple quality signals:
HTML validity: Run through the W3C Nu Html Checker (validator.w3.org/nu). Zero errors is the standard. Warnings about lacking charset or lang are blocking issues.
Accessibility tree audit: In Chrome DevTools, open the Accessibility panel and inspect each element's computed role, name, and state. Every interactive element must have a computed accessible name.
Automated accessibility: Run axe-core (via browser extension or axe.run() in test suite). Aim for zero violations. axe catches ~57% of WCAG 2.1 issues automatically.
Manual keyboard test: Tab through the entire page without a mouse. Every interactive element must be reachable. Focus order must match visual reading order. No focus traps except intentional modal traps.
Screen reader test: Use NVDA on Firefox (free) and VoiceOver on Safari. Navigate by headings (H key in NVDA), by landmarks (D key), and by form elements (F key). All content must be announced correctly.
Contrast and zoom: Text must meet 4.5:1 contrast ratio for normal text (3:1 for large text at 18px/14px bold). Page must be usable at 200% browser zoom without horizontal scrolling.
Output Format
When helping a user implement HTML semantics, produce output in this structure:
## Semantic HTML Analysis
### Page/Component: [Name]
#### Landmark Structure
| Role | Element | Accessible Name Source | Notes |
|---------------|-------------|----------------------------|--------------------------------|
| banner | <header> | (implicit -- no label needed) | Site-wide header |
| navigation | <nav> | aria-label="Primary" | Main nav with skip link target |
| main | <main> | (implicit) | One per page |
| complementary | <aside> | aria-labelledby="sidebar-h"| Related posts sidebar |
| contentinfo | <footer> | (implicit) | Site-wide footer |
#### Heading Outline
h1: [Page Title]
h2: [Section One]
h3: [Subsection A]
h3: [Subsection B]
h2: [Section Two]
h3: [Subsection C]
#### Element Selection Rationale
| Content Block | Chosen Element | Rejected Alternative | Reason |
|----------------------|------------------------|-----------------------|-------------------------------------|
| Blog post container | <article> | <section> | Independently distributable |
| Author bio | <aside> | <div> | Tangentially related to post |
| Published date | <time datetime="..."> | <span> | Machine-readable temporal data |
| Expandable FAQ item | <details>/<summary> | <div> + JavaScript | Native disclosure semantics |
#### Corrected HTML
```html
[Full corrected markup here]
Accessibility Checklist
☐To do:
Single <h1> present and descriptive
☐To do:
No heading levels skipped
☐To do:
All <nav> elements have unique accessible names
☐To do:
<main> landmark present
☐To do:
All images have meaningful alt text or alt="" if decorative
☐To do:
All interactive elements reachable by keyboard
☐To do:
Focus order matches visual order
☐To do:
No content relies on color alone to convey meaning
☐To do:
ARIA roles do not conflict with native element semantics
☐To do:
Page language declared: <html lang="en">
Validation Commands
bash
# Run axe-core in browser console
const axe = await import('https://cdn.jsdelivr.net/npm/axe-core/axe.min.js');
axe.run().then(r => console.table(r.violations));
# W3C validator via CLI (vnu.jar)
java -jar vnu.jar --format json index.html
Page/Component: Blog Article -- "Understanding Async/Await in JavaScript"
Issues Found
Issue
Severity
WCAG Criterion
<h3> used as only/first heading -- h1 missing
Critical
1.3.1 Info and Relationships
All landmark regions using <div>
Critical
1.3.6 Identify Purpose
<img> missing alt attribute
Critical
1.1.1 Non-text Content
Navigation not wrapped in <nav>
Serious
1.3.1
Published date not machine-readable
Moderate
1.3.1
Code block not in <pre><code>
Moderate
1.3.1
Related articles is a list, not bare divs
Moderate
1.3.1
<b>Important:</b> should be <strong>
Minor
1.3.1
Author box semantically disconnected from article
Minor
1.3.1
No skip navigation link
Serious
2.4.1 Bypass Blocks
<html lang> not shown -- must be present
Critical
3.1.1 Language of Page
Landmark Structure (Corrected)
Role
Element
Accessible Name
Notes
banner
<header>
(implicit)
Site-wide -- wraps logo + nav
navigation
<nav>
aria-label="Primary"
Inside <header>
main
<main>
(implicit)
Wraps article + sidebar
contentinfo
<footer>
(implicit)
Site-wide footer
Note: the sidebar <aside> is nested inside <main> -- this is valid and expected. The <aside> in this case contains "Related Articles" which is tangentially related content.
Heading Outline (Corrected)
h1: Understanding Async/Await in JavaScript (article title)
h2: Related Articles (sidebar section label -- visually hidden acceptable)
Element Selection Rationale
Content Block
Chosen Element
Rejected
Reason
Page wrapper for post
<article>
<div class="main-article">
Self-contained, syndicatable blog post
Article title
<h1>
<h3>
First and only heading -- must be h1
Published date
<time datetime="2024-12-15">
<span>
Machine-readable ISO date for SEO + AT
Author bio box
<aside> inside <article>
<div class="author-box">
Supplementary to the article content
Author photo
<img alt="Jane Smith">
<img> with no alt
Meaningful image -- person identity
Important note
<p> with <strong>
<div> with <b>
<strong> = importance; <div> invalid inside flow as anonymous wrapper
<h3> → <h1>: The article title is the primary heading of the page. Using <h3> means screen reader users navigating by headings hear "heading level 3" with no h1 or h2 above it -- implying they are deep in a document hierarchy that does not exist.
<div class="nav"> → <nav aria-label="Primary"><ul>...</ul></nav>: Screen readers provide shortcut keys to jump to navigation landmarks. Without <nav>, keyboard users cannot skip to or past the navigation. The <ul> inside communicates that these are list items -- NVDA announces "list of 3 items" which helps users gauge the nav size.
<span>Published: December 15, 2024</span> → <time datetime="2024-12-15">: Google Search uses <time> to extract publication dates for article rich results. The datetime attribute provides an unambiguous ISO 8601 date regardless of how it is displayed (localized, relative, etc.).
<div class="code-block"> → <figure><figcaption>...<pre><code>: <pre> preserves the indentation and whitespace that is semantically meaningful in code. <code> marks the content as computer code. <figure> with <figcaption> gives the sample an accessible description and places it as a referenceable content block.
<img src="jane.jpg"> → <img src="jane.jpg" alt="Jane Smith">: The missing alt attribute causes NVDA to read the full file path "jane dot jpg" which conveys no useful information. Alt text "Jane Smith" identifies the person depicted.
Accessibility Checklist
☑Done:
Single <h1> present and descriptive
☑Done:
No heading levels skipped (h1 → h2 only)
☑Done:
All <nav> elements have unique accessible names
☑Done:
<main> landmark present with id for skip link target
☑Done:
All images have meaningful alt text
☑Done:
All interactive elements reachable by keyboard (native elements used throughout)
☑Done:
Skip navigation link is first focusable element
☑Done:
<html lang="en"> declared
☑Done:
ARIA roles not needed -- native elements carry all required semantics
☑Done:
<article> heading restarts at <h1> for the article, <h2> for nested sections