CrackedRuby CrackedRuby

Overview

Semantic HTML refers to the practice of using HTML markup to reinforce the meaning of content rather than merely defining its presentation. Each semantic element describes the type of content it contains, creating a structured document that both humans and machines can interpret without relying on visual styling.

The concept emerged as web standards evolved beyond the presentation-focused HTML of the 1990s. HTML5 introduced numerous semantic elements like <article>, <section>, <nav>, and <aside> to replace the generic <div> elements that previously dominated web markup. This shift separated content structure from visual presentation, aligning with the CSS separation of concerns principle.

Semantic HTML serves three primary audiences. Screen readers and assistive technologies parse semantic elements to construct an accessibility tree, enabling users to navigate content efficiently. Search engines interpret semantic markup to understand page structure and content hierarchy, influencing search rankings. Human developers benefit from self-documenting code where element names indicate content purpose.

<!-- Non-semantic approach -->
<div class="header">
  <div class="nav">
    <div class="nav-item">Home</div>
  </div>
</div>

<!-- Semantic approach -->
<header>
  <nav>
    <a href="/">Home</a>
  </nav>
</header>

The semantic version requires no class names to convey structure. The <header> element explicitly identifies page header content. The <nav> element marks navigation functionality. Assistive technologies recognize these elements and provide appropriate navigation shortcuts to users.

Consider a blog post structure. Non-semantic markup might use nested <div> elements with descriptive class names. Semantic markup uses <article> for the post, <header> for metadata, <time> for publication date, and <section> for content segments. This structure creates machine-readable metadata that search engines index and screen readers announce.

Key Principles

Semantic HTML operates on the principle that markup should describe content meaning rather than appearance. Every element selection should answer "what is this content?" not "how should this content look?" Visual presentation belongs in CSS, while HTML defines document structure and meaning.

Document structure follows a hierarchical organization where elements nest to create parent-child relationships. The <html> root contains <head> and <body>. The body contains sectioning elements like <header>, <main>, <aside>, and <footer>. These elements group related content and establish document landmarks.

Heading elements (<h1> through <h6>) create a document outline that represents content hierarchy. Each heading level indicates relative importance and nesting. Screen readers generate navigation menus from heading structure, allowing users to skip between sections. Search engines weight content based on heading hierarchy, treating <h1> content as more significant than <h6>.

<article>
  <h1>Main Article Title</h1>
  <section>
    <h2>First Section</h2>
    <p>Section content...</p>
    <section>
      <h3>Subsection</h3>
      <p>Subsection content...</p>
    </section>
  </section>
  <section>
    <h2>Second Section</h2>
    <p>Section content...</p>
  </section>
</article>

The accessibility tree represents the semantic structure browsers expose to assistive technologies. When a browser parses HTML, it constructs a DOM tree and derives an accessibility tree containing only semantically meaningful nodes. Elements like <div> and <span> typically do not appear in this tree unless given explicit ARIA roles. Semantic elements automatically provide appropriate roles, names, and properties.

Sectioning content divides documents into logical regions. The <article> element represents self-contained content that makes sense independently, like blog posts or news articles. The <section> element groups thematically related content, typically with a heading. The <aside> element contains tangentially related content, like sidebars or pull quotes. The <nav> element marks navigation links.

Landmark roles emerge from certain semantic elements. The <header> element creates a banner landmark when it's a direct child of <body>. The <main> element creates a main content landmark. The <nav> element creates a navigation landmark. These landmarks enable assistive technology users to jump directly to major page regions without traversing intervening content.

Text-level semantics provide meaning for inline content. The <strong> element indicates importance, urgency, or seriousness, while <em> indicates stress emphasis. The <mark> element highlights relevant text. The <time> element represents dates and times in a machine-readable format. The <abbr> element defines abbreviations with full expansions.

<p>
  The meeting is scheduled for 
  <time datetime="2025-10-15T14:00">October 15th at 2:00 PM</time>.
  This is <strong>extremely important</strong> for all team members.
  We will discuss <abbr title="Application Programming Interface">API</abbr> design.
</p>

Form semantics establish relationships between controls and labels. The <label> element associates descriptive text with form controls, improving accessibility and expanding click targets. The <fieldset> element groups related controls, while <legend> provides group descriptions. The <output> element displays calculation results.

Ruby Implementation

Ruby web frameworks generate HTML through templating systems that abstract HTML creation. Rails provides view helpers that produce semantic markup, while templating engines like ERB, HAML, and Slim offer different syntaxes for HTML generation. Understanding how these tools create semantic HTML ensures generated markup maintains proper structure.

Rails view helpers encapsulate semantic patterns in reusable methods. The content_tag helper generates arbitrary HTML elements with proper escaping and attribute handling. Rather than interpolating strings, view helpers produce safe HTML that respects semantic structure.

# Generating semantic structure with content_tag
content_tag :article, class: 'blog-post' do
  concat content_tag(:header) {
    content_tag :h1, @post.title
  }
  concat content_tag(:section) {
    content_tag :p, @post.body
  }
  concat content_tag(:footer) {
    content_tag :time, @post.published_at, datetime: @post.published_at.iso8601
  }
end

The tag helper creates void elements (self-closing tags) like <br>, <img>, and <input>. It ensures proper formatting without requiring closing tags. Combined with content_tag, it covers all HTML generation needs while maintaining semantic correctness.

Rails form helpers generate semantic form markup automatically. The form_with helper creates forms with proper <label> associations. Text fields, select boxes, and other inputs receive appropriate semantic attributes.

<%= form_with model: @article, local: true do |form| %>
  <fieldset>
    <legend>Article Details</legend>
    
    <%= form.label :title %>
    <%= form.text_field :title, required: true %>
    
    <%= form.label :published_at, "Publication Date" %>
    <%= form.datetime_field :published_at %>
    
    <%= form.label :category_id, "Category" %>
    <%= form.collection_select :category_id, Category.all, :id, :name, 
                                prompt: "Select a category" %>
  </fieldset>
  
  <%= form.submit "Publish Article" %>
<% end %>

HAML provides indentation-based syntax that naturally enforces proper nesting. Its concise notation reduces boilerplate while maintaining semantic clarity. HAML's structure mirrors the semantic hierarchy visually through indentation.

# HAML semantic structure
%article.blog-post
  %header
    %h1= @post.title
    %p.byline
      By
      %span.author= @post.author
      on
      %time{ datetime: @post.published_at.iso8601 }= @post.published_at.strftime("%B %d, %Y")
  
  %section.content
    = simple_format @post.body
  
  %aside.related
    %h2 Related Articles
    %ul
      - @post.related.each do |related|
        %li
          %a{ href: article_path(related) }= related.title
  
  %footer
    %p.tags
      - @post.tags.each do |tag|
        %span.tag= tag.name

Slim offers even more concise syntax while preserving semantic structure. Its Ruby-like appearance integrates naturally with Rails views. Slim enforces semantic HTML through its syntax constraints, making it difficult to create invalid nesting.

# Slim semantic structure
article.blog-post
  header
    h1 = @post.title
    nav.article-nav
      a href=edit_article_path(@post) Edit
      a href=article_path(@post) data-method="delete" Delete
  
  section.content
    == @post.body_html
  
  footer
    p
      | Published 
      time datetime=@post.published_at.iso8601
        = @post.published_at.strftime("%B %d, %Y")

Rails content helpers generate semantic patterns for common scenarios. The link_to helper creates proper anchor tags with accessible text. The image_tag helper requires alt text for accessibility compliance. These helpers make it easier to create semantically correct markup than to create incorrect markup.

# Semantic navigation with Rails helpers
<nav aria-label="Main navigation">
  <ul>
    <li><%= link_to "Home", root_path, aria: { current: "page" } if current_page?(root_path) %></li>
    <li><%= link_to "Articles", articles_path %></li>
    <li><%= link_to "About", about_path %></li>
  </ul>
</nav>

# Semantic image with required alt text
<figure>
  <%= image_tag @product.image_url, alt: @product.image_description %>
  <figcaption><%= @product.image_caption %></figcaption>
</figure>

View components encapsulate semantic patterns in reusable objects. A component might render an article card with consistent semantic structure across the application. This approach centralizes semantic decisions and prevents inconsistent markup.

# app/components/article_card_component.rb
class ArticleCardComponent < ViewComponent::Base
  def initialize(article:)
    @article = article
  end
  
  def call
    tag.article(class: "article-card") do
      safe_join([
        render_header,
        render_content,
        render_footer
      ])
    end
  end
  
  private
  
  def render_header
    tag.header do
      tag.h2 { link_to @article.title, article_path(@article) }
    end
  end
  
  def render_content
    tag.section(class: "excerpt") do
      tag.p @article.excerpt
    end
  end
  
  def render_footer
    tag.footer do
      tag.time @article.published_at.strftime("%B %d, %Y"),
               datetime: @article.published_at.iso8601
    end
  end
end

Practical Examples

Blog post markup demonstrates semantic structure for article content. The document uses <article> as the container, <header> for metadata, <section> for content blocks, and <aside> for tangential information. Each element choice reflects content purpose rather than visual layout.

<article itemscope itemtype="http://schema.org/BlogPosting">
  <header>
    <h1 itemprop="headline">Understanding Database Indexing</h1>
    
    <p class="byline">
      By <span itemprop="author">Sarah Chen</span>
      on <time itemprop="datePublished" datetime="2025-10-10">October 10, 2025</time>
    </p>
    
    <p itemprop="description">
      An exploration of how database indexes improve query performance 
      and the trade-offs involved in index design.
    </p>
  </header>
  
  <section>
    <h2>What Are Indexes?</h2>
    <p>
      Database indexes create additional data structures that enable 
      faster data retrieval at the cost of additional storage and 
      slower write operations.
    </p>
  </section>
  
  <section>
    <h2>Types of Indexes</h2>
    
    <section>
      <h3>B-Tree Indexes</h3>
      <p>
        B-tree indexes maintain sorted data in a tree structure, 
        supporting efficient range queries and ordered retrieval.
      </p>
    </section>
    
    <section>
      <h3>Hash Indexes</h3>
      <p>
        Hash indexes provide O(1) lookup for exact match queries 
        but cannot support range operations.
      </p>
    </section>
  </section>
  
  <aside>
    <h2>Performance Tip</h2>
    <p>
      Index columns used in WHERE clauses, JOIN conditions, 
      and ORDER BY clauses to maximize query performance.
    </p>
  </aside>
  
  <footer>
    <p>
      Tags: 
      <a href="/tags/databases" rel="tag">databases</a>, 
      <a href="/tags/performance" rel="tag">performance</a>
    </p>
  </footer>
</article>

The article uses microdata attributes (itemscope, itemtype, itemprop) to enhance semantic meaning with structured data. Search engines parse this markup to generate rich search results. The nested sections create a document outline that screen readers navigate hierarchically.

E-commerce product pages require semantic markup for product information, pricing, availability, and user reviews. Proper semantic structure enables rich snippets in search results and improves accessibility for users comparing products.

<article itemscope itemtype="http://schema.org/Product">
  <header>
    <h1 itemprop="name">Professional Noise-Canceling Headphones</h1>
  </header>
  
  <figure>
    <img src="headphones.jpg" 
         alt="Black over-ear headphones with silver accents"
         itemprop="image">
    <figcaption>Available in black, silver, and navy</figcaption>
  </figure>
  
  <section>
    <h2>Product Details</h2>
    <p itemprop="description">
      Premium wireless headphones with active noise cancellation, 
      30-hour battery life, and studio-quality audio.
    </p>
    
    <dl>
      <dt>Weight</dt>
      <dd itemprop="weight">250 grams</dd>
      
      <dt>Battery Life</dt>
      <dd>30 hours</dd>
      
      <dt>Connectivity</dt>
      <dd>Bluetooth 5.2, 3.5mm jack</dd>
    </dl>
  </section>
  
  <section itemprop="offers" itemscope itemtype="http://schema.org/Offer">
    <h2>Pricing</h2>
    <p>
      <data itemprop="price" value="299.99">$299.99</data>
      <link itemprop="availability" href="http://schema.org/InStock">
      <span class="availability">In Stock</span>
    </p>
    
    <form action="/cart" method="post">
      <label for="quantity">Quantity:</label>
      <input type="number" id="quantity" name="quantity" 
             min="1" max="10" value="1">
      <button type="submit">Add to Cart</button>
    </form>
  </section>
  
  <section>
    <h2>Customer Reviews</h2>
    
    <article itemprop="review" itemscope itemtype="http://schema.org/Review">
      <header>
        <h3 itemprop="name">Excellent sound quality</h3>
        <p>
          By <span itemprop="author">Mike Johnson</span>
          on <time itemprop="datePublished" datetime="2025-09-15">September 15, 2025</time>
        </p>
        <p>
          Rating: 
          <data itemprop="reviewRating" itemscope itemtype="http://schema.org/Rating">
            <span itemprop="ratingValue">5</span>/
            <span itemprop="bestRating">5</span>
          </data>
        </p>
      </header>
      <p itemprop="reviewBody">
        These headphones exceeded my expectations. The noise cancellation 
        works remarkably well, and the audio quality rivals studio monitors.
      </p>
    </article>
  </section>
</article>

Application dashboards require semantic structure for navigation, data tables, and interactive controls. Proper landmark roles help assistive technology users navigate complex interfaces efficiently.

<body>
  <header role="banner">
    <h1>Analytics Dashboard</h1>
    <nav aria-label="Account menu">
      <ul>
        <li><a href="/profile">Profile</a></li>
        <li><a href="/settings">Settings</a></li>
        <li><a href="/logout">Logout</a></li>
      </ul>
    </nav>
  </header>
  
  <nav aria-label="Main navigation">
    <ul>
      <li><a href="/dashboard" aria-current="page">Dashboard</a></li>
      <li><a href="/reports">Reports</a></li>
      <li><a href="/users">Users</a></li>
    </ul>
  </nav>
  
  <main>
    <h2>Overview</h2>
    
    <section aria-labelledby="metrics-heading">
      <h3 id="metrics-heading">Key Metrics</h3>
      
      <div class="metric-cards">
        <article>
          <h4>Total Users</h4>
          <p><data value="15234">15,234</data></p>
          <p class="change">
            <span aria-label="Increased by">↑</span> 
            <data value="8.3">8.3%</data> from last month
          </p>
        </article>
        
        <article>
          <h4>Revenue</h4>
          <p><data value="432890">$432,890</data></p>
          <p class="change">
            <span aria-label="Increased by">↑</span> 
            <data value="12.5">12.5%</data> from last month
          </p>
        </article>
      </div>
    </section>
    
    <section aria-labelledby="activity-heading">
      <h3 id="activity-heading">Recent Activity</h3>
      
      <table>
        <caption class="sr-only">Recent user activity log</caption>
        <thead>
          <tr>
            <th scope="col">User</th>
            <th scope="col">Action</th>
            <th scope="col">Time</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>Sarah Chen</td>
            <td>Created new report</td>
            <td><time datetime="2025-10-10T14:32">2:32 PM</time></td>
          </tr>
          <tr>
            <td>Mike Johnson</td>
            <td>Updated user permissions</td>
            <td><time datetime="2025-10-10T14:28">2:28 PM</time></td>
          </tr>
        </tbody>
      </table>
    </section>
  </main>
  
  <aside aria-labelledby="notifications-heading">
    <h3 id="notifications-heading">Notifications</h3>
    <ul>
      <li>
        <article>
          <h4>System Update</h4>
          <p>Scheduled maintenance tonight at <time datetime="2025-10-10T23:00">11:00 PM</time></p>
        </article>
      </li>
    </ul>
  </aside>
  
  <footer role="contentinfo">
    <p>&copy; 2025 Analytics Platform. <a href="/privacy">Privacy Policy</a></p>
  </footer>
</body>

Security Implications

Semantic HTML affects security through its interaction with content sanitization, XSS prevention, and accessibility features. Improper handling of semantic elements creates attack vectors that compromise user data and system integrity.

Cross-site scripting attacks exploit insufficient input sanitization. When user-generated content renders without proper escaping, attackers inject malicious scripts. Semantic elements do not inherently prevent XSS, but frameworks that generate semantic HTML typically include automatic escaping.

# Vulnerable: Direct interpolation without escaping
<article>
  <h1><%= @post.title %></h1>
  <section>
    <%= @post.body %>  # XSS vulnerability if body contains scripts
  </section>
</article>

# Secure: Automatic escaping with ERB
<article>
  <h1><%= @post.title %></h1>  # Automatically escaped
  <section>
    <%== sanitize @post.body, tags: %w[p strong em a], 
                              attributes: %w[href] %>
  </section>
</article>

Rails sanitize helper strips dangerous HTML while preserving semantic structure. It removes script tags, event handlers, and dangerous attributes while allowing safe semantic elements. Configuration controls which elements and attributes pass through sanitization.

Content Security Policy headers restrict resource loading to prevent injected scripts from executing. CSP directives specify allowed script sources, preventing inline scripts from running even if they bypass HTML sanitization.

# config/initializers/content_security_policy.rb
Rails.application.config.content_security_policy do |policy|
  policy.default_src :self
  policy.script_src  :self, :https
  policy.style_src   :self, :https, :unsafe_inline
  policy.img_src     :self, :https, :data
  policy.font_src    :self, :https, :data
  policy.connect_src :self, :https
end

ARIA attributes create security concerns when misused. The aria-label and aria-describedby attributes accept arbitrary text that screen readers announce. If this text contains user input without sanitization, attackers might manipulate screen reader output to deceive users.

<!-- Vulnerable: Unsanitized user input in ARIA -->
<button aria-label="<%= @action.name %>">
  Execute Action
</button>

<!-- Secure: Sanitized or validated input -->
<button aria-label="<%= sanitize(@action.name, tags: []) %>">
  Execute Action
</button>

Link relationships affect security through the rel attribute. The rel="noopener noreferrer" combination prevents malicious sites accessed through links from accessing the referring page's window object. This protection matters for user-generated content containing external links.

<!-- Vulnerable: Opens security hole for target page -->
<a href="<%= @link.url %>" target="_blank">
  <%= @link.title %>
</a>

<!-- Secure: Prevents window.opener access -->
<a href="<%= @link.url %>" target="_blank" rel="noopener noreferrer">
  <%= @link.title %>
</a>

Form security depends on proper semantic structure combined with server-side validation. The autocomplete attribute controls whether browsers store sensitive information. Forms handling passwords, credit cards, or personal data should disable autocomplete for those specific fields.

<form action="/payment" method="post">
  <fieldset>
    <legend>Payment Information</legend>
    
    <label for="card-number">Card Number</label>
    <input type="text" 
           id="card-number" 
           name="card_number"
           autocomplete="cc-number"
           inputmode="numeric"
           pattern="[0-9]{13,19}"
           required>
    
    <label for="cvv">CVV</label>
    <input type="text"
           id="cvv"
           name="cvv"
           autocomplete="cc-csc"
           inputmode="numeric"
           pattern="[0-9]{3,4}"
           required>
  </fieldset>
  
  <%= hidden_field_tag :authenticity_token, form_authenticity_token %>
  <button type="submit">Process Payment</button>
</form>

The form includes CSRF token protection through Rails authenticity token. This semantic pattern combines with server-side validation to prevent form submission forgery. The autocomplete attributes use standardized values that browsers recognize for secure password manager integration.

Design Considerations

Element selection determines document structure and semantic meaning. Each HTML element carries implicit semantics that affect accessibility, SEO, and code maintainability. Choosing between elements requires understanding their semantic purposes and use cases.

The <div> versus semantic element decision depends on whether content has inherent meaning. Use <div> only for purely presentational grouping without semantic significance. Use semantic elements like <article>, <section>, <nav>, or <aside> when content serves a specific purpose.

<!-- Appropriate div use: purely presentational wrapper -->
<article>
  <h1>Article Title</h1>
  <div class="columns">  <!-- Layout container only -->
    <section>Content column one</section>
    <section>Content column two</section>
  </div>
</article>

<!-- Inappropriate div use: semantic meaning present -->
<div class="blog-post">  <!-- Should be <article> -->
  <div class="post-header">  <!-- Should be <header> -->
    <div class="post-title">Title</div>  <!-- Should be <h1> -->
  </div>
</div>

Heading hierarchy requires careful planning. Each page should have exactly one <h1> representing the primary topic. Subsequent headings follow numerical order without skipping levels. Heading structure creates the document outline that assistive technologies use for navigation.

<!-- Correct heading hierarchy -->
<article>
  <h1>Web Application Security</h1>
  
  <section>
    <h2>Authentication</h2>
    <section>
      <h3>Password Storage</h3>
      <p>Content...</p>
    </section>
    <section>
      <h3>Session Management</h3>
      <p>Content...</p>
    </section>
  </section>
  
  <section>
    <h2>Authorization</h2>
    <p>Content...</p>
  </section>
</article>

<!-- Incorrect: skips heading levels -->
<article>
  <h1>Web Application Security</h1>
  <h4>Password Storage</h4>  <!-- Skips h2 and h3 -->
</article>

The <section> versus <article> distinction hinges on content independence. Use <article> for self-contained content that makes sense when syndicated or shared independently. Use <section> for thematic groupings within a larger context. Articles can contain sections, and sections can contain articles.

<!-- Article containing sections -->
<article>
  <h1>Database Design Patterns</h1>
  <section>
    <h2>Normalization</h2>
    <p>Content about normalization...</p>
  </section>
  <section>
    <h2>Denormalization</h2>
    <p>Content about denormalization...</p>
  </section>
</article>

<!-- Section containing articles (blog index) -->
<section>
  <h1>Recent Posts</h1>
  <article>
    <h2>First Post Title</h2>
    <p>Excerpt...</p>
  </article>
  <article>
    <h2>Second Post Title</h2>
    <p>Excerpt...</p>
  </article>
</section>

Button versus link semantics reflect different interaction patterns. Links navigate to different URLs or page locations. Buttons trigger actions, submit forms, or modify interface state. Misusing buttons as links breaks keyboard navigation and confuses screen reader users.

<!-- Correct: Link for navigation -->
<a href="/articles/123">Read More</a>

<!-- Correct: Button for action -->
<button type="button" onclick="toggleComments()">Show Comments</button>

<!-- Incorrect: Button for navigation -->
<button onclick="location.href='/articles/123'">Read More</button>

<!-- Incorrect: Link for action -->
<a href="#" onclick="toggleComments()">Show Comments</a>

List semantics distinguish between ordered, unordered, and description lists. Ordered lists (<ol>) represent sequences where order matters. Unordered lists (<ul>) represent sets where order is arbitrary. Description lists (<dl>) represent term-definition pairs.

<!-- Ordered list: steps in sequence -->
<ol>
  <li>Clone the repository</li>
  <li>Install dependencies</li>
  <li>Run the test suite</li>
</ol>

<!-- Unordered list: no inherent order -->
<ul>
  <li>Ruby</li>
  <li>JavaScript</li>
  <li>Python</li>
</ul>

<!-- Description list: term-definition pairs -->
<dl>
  <dt>API</dt>
  <dd>Application Programming Interface</dd>
  
  <dt>REST</dt>
  <dd>Representational State Transfer</dd>
</dl>

Table usage requires genuine tabular data. Tables represent relationships between row and column headers. Using tables for layout creates accessibility problems because screen readers interpret content as data relationships. Use CSS Grid or Flexbox for layout instead.

<!-- Correct: actual tabular data -->
<table>
  <caption>Server Response Times</caption>
  <thead>
    <tr>
      <th scope="col">Endpoint</th>
      <th scope="col">Avg Response (ms)</th>
      <th scope="col">95th Percentile (ms)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">/api/users</th>
      <td>45</td>
      <td>120</td>
    </tr>
    <tr>
      <th scope="row">/api/posts</th>
      <td>67</td>
      <td>180</td>
    </tr>
  </tbody>
</table>

Common Pitfalls

Semantic element misuse creates accessibility problems and confuses search engines. Developers frequently misapply semantic elements based on visual design rather than content meaning. This pattern produces markup that looks correct but fails accessibility requirements.

Using multiple <main> elements on a single page violates HTML specifications. Each page should have exactly one main landmark representing primary content. Multiple main elements confuse screen readers attempting to navigate to main content.

<!-- Incorrect: multiple main elements -->
<main>
  <h1>Page Title</h1>
  <p>Introduction</p>
</main>
<main>
  <h2>Content Section</h2>
  <p>More content</p>
</main>

<!-- Correct: single main with sections -->
<main>
  <h1>Page Title</h1>
  <p>Introduction</p>
  
  <section>
    <h2>Content Section</h2>
    <p>More content</p>
  </section>
</main>

Heading order violations break document structure. Developers skip heading levels to achieve desired font sizes, destroying the semantic outline. This forces screen reader users to navigate incomplete heading structures.

<!-- Incorrect: skips h2 for styling -->
<article>
  <h1>Main Title</h1>
  <h3>Subtitle</h3>  <!-- Skips h2 -->
  <p>Content</p>
</article>

<!-- Correct: use h2, adjust CSS -->
<article>
  <h1>Main Title</h1>
  <h2 class="subtitle">Subtitle</h2>  <!-- Proper hierarchy -->
  <p>Content</p>
</article>

Empty link text and buttons harm accessibility. Assistive technologies announce link purpose from link text. Using "click here" or "read more" provides no context about destination or action.

<!-- Incorrect: meaningless link text -->
<p>
  To learn about database indexing, 
  <a href="/articles/indexing">click here</a>.
</p>

<!-- Correct: descriptive link text -->
<p>
  Learn about <a href="/articles/indexing">database indexing strategies</a>.
</p>

<!-- Incorrect: icon-only button -->
<button type="button">
  <svg>...</svg>
</button>

<!-- Correct: accessible icon button -->
<button type="button" aria-label="Delete item">
  <svg aria-hidden="true">...</svg>
</button>

Excessive ARIA attributes override native semantics. Adding ARIA roles to semantic elements creates redundancy or conflicts. The first rule of ARIA states: use native HTML elements instead of adding ARIA when possible.

<!-- Incorrect: redundant ARIA on semantic element -->
<nav role="navigation" aria-label="navigation">
  <ul role="list">
    <li role="listitem">
      <a href="/" role="link">Home</a>
    </li>
  </ul>
</nav>

<!-- Correct: native semantics without redundant ARIA -->
<nav aria-label="Main">
  <ul>
    <li><a href="/">Home</a></li>
  </ul>
</nav>

Misusing sectioning elements for styling breaks document structure. The <section> element requires a heading to establish its place in the document outline. Using sections as styling hooks without headings creates invalid structure.

<!-- Incorrect: section without heading -->
<article>
  <h1>Article Title</h1>
  <section class="columns">  <!-- No heading, just layout -->
    <p>Column one</p>
    <p>Column two</p>
  </section>
</article>

<!-- Correct: div for layout, section for semantic grouping -->
<article>
  <h1>Article Title</h1>
  <div class="columns">
    <section>
      <h2>First Topic</h2>
      <p>Column one content</p>
    </section>
    <section>
      <h2>Second Topic</h2>
      <p>Column two content</p>
    </section>
  </div>
</article>

Form labels disconnected from inputs reduce accessibility and usability. Every form control requires an associated label. The label text announces the control's purpose to screen readers and expands the clickable area.

<!-- Incorrect: label without association -->
<label>Email Address</label>
<input type="email" name="email">

<!-- Correct: explicit label association -->
<label for="email">Email Address</label>
<input type="email" id="email" name="email">

<!-- Also correct: implicit association -->
<label>
  Email Address
  <input type="email" name="email">
</label>

Missing alternative text for images blocks screen reader users from understanding visual content. Every image requires alt text describing its content or purpose. Decorative images should have empty alt attributes to prevent screen readers from announcing filenames.

<!-- Incorrect: missing alt attribute -->
<img src="graph.png">

<!-- Correct: descriptive alt text -->
<img src="graph.png" 
     alt="Line graph showing 40% increase in user engagement from January to March">

<!-- Correct: empty alt for decorative image -->
<img src="decorative-border.png" alt="">

Table headers without scope attributes confuse relationships between data cells. The scope attribute indicates whether a header applies to a row, column, row group, or column group. Complex tables require scope or headers/id associations for accessibility.

<!-- Incorrect: headers without scope -->
<table>
  <tr>
    <th>Name</th>
    <th>Age</th>
  </tr>
  <tr>
    <td>Sarah</td>
    <td>32</td>
  </tr>
</table>

<!-- Correct: explicit scope attributes -->
<table>
  <thead>
    <tr>
      <th scope="col">Name</th>
      <th scope="col">Age</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">Sarah</th>
      <td>32</td>
    </tr>
  </tbody>
</table>

Reference

Sectioning Elements

Element Purpose Usage Guidelines
article Self-contained content Blog posts, news articles, user comments, independent widgets
section Thematic content grouping Chapter sections, tabs, parts of an article with headings
nav Navigation links Primary site navigation, pagination, table of contents
aside Tangentially related content Sidebars, pull quotes, related links, advertising
header Introductory content Page headers, article headers, section headers with metadata
footer Footer content Copyright, contact info, related links, document metadata
main Primary content Page main content, one per page, excludes repeated content
address Contact information Email, physical address, social links for article or page author

Text-Level Semantics

Element Purpose Usage Guidelines
strong Strong importance Critical warnings, important information requiring attention
em Stress emphasis Vocal stress, changing meaning through emphasis
mark Highlighted relevance Search results highlighting, referenced text in quotes
code Computer code Inline code snippets, variable names, file paths
kbd Keyboard input Keys user should press, keyboard shortcuts
samp Program output Terminal output, program results, log entries
var Mathematical variable Variables in equations, placeholders in code examples
time Date or time Publication dates, event times, deadlines
abbr Abbreviation Acronyms and abbreviations with title attribute for full form
cite Citation title Book titles, article titles, work titles being referenced
q Inline quotation Short quotes, automatically adds quotation marks
blockquote Block quotation Extended quotes, use cite attribute for source URL
del Deleted content Removed text in document revisions
ins Inserted content Added text in document revisions
sub Subscript Mathematical subscripts, chemical formulas
sup Superscript Mathematical exponents, ordinal indicators

Form Elements

Element Purpose Usage Guidelines
label Control label Required for all inputs, use for attribute or wrap input
fieldset Control grouping Related form controls, radio button groups, checkbox groups
legend Fieldset caption Describes purpose of fieldset, first child of fieldset
input User input Many types, always include type attribute and associated label
select Dropdown selection Multiple options, group with optgroup, requires label
textarea Multi-line text Long-form text input, specify rows and cols or use CSS
button Clickable button Always specify type: submit, reset, or button
output Calculation result Display calculation output, associate with form attribute
datalist Input suggestions Autocomplete options for input, use list attribute on input
progress Progress indicator Operation progress, use value and max attributes
meter Scalar measurement Disk usage, vote percentages, use value, min, max

Heading Hierarchy

Pattern Structure Use Case
Single section h1 > p Simple page with no subsections
Two levels h1 > h2 > p Page with major sections
Three levels h1 > h2 > h3 > p Complex page with nested sections
Article outline article > h1, section > h2, section > h3 Self-contained article structure
Nested articles section > h1, article > h2 Blog index with article previews

Decision Matrix

If Content Is Use Element Not Element
Self-contained, redistributable article section, div
Navigation links nav div, aside
Thematically grouped with heading section div
Tangentially related aside section
Primary page content main section, div
Introductory or metadata header section, div
Appendix or metadata footer section, div
Important emphasis strong b, span
Stress emphasis em i, span
User input data input div with contenteditable
Tabular relationships table div with CSS grid
Ordered steps or ranking ol ul, div
Unordered set ul ol, div
Term-definition pairs dl, dt, dd ul, table

ARIA Landmark Roles

HTML Element Implicit Role Additional Considerations
header banner when in body None otherwise, use aria-label for multiple headers
nav navigation Use aria-label to distinguish multiple nav elements
main main Only one per page, skip link target
aside complementary Use aria-labelledby for identification
footer contentinfo when in body None otherwise, use aria-label for multiple footers
section region when labeled Requires aria-labelledby or aria-label
form form when labeled Use aria-label or aria-labelledby for identification
search search Use role=search on form element

Accessibility Attributes

Attribute Purpose Example Values
aria-label Accessible name "Main navigation", "Close dialog"
aria-labelledby Name from element ID id="section-heading"
aria-describedby Additional description id="help-text"
aria-hidden Hide from screen readers true, false
aria-current Current item indicator page, step, location, date, time
role Explicit role button, link, navigation, main
aria-live Dynamic content announcements polite, assertive, off
aria-expanded Expandable state true, false
aria-controls Controlled element ID id="panel-1"

Microdata Schema

Property Element Pattern Purpose
itemscope article, div Defines item boundary
itemtype article, div Schema type URL from schema.org
itemprop span, data, time, meta Property name and value
Article schema article with itemprop headline, author, datePublished Blog post structured data
Product schema article with itemprop name, price, availability E-commerce product data
Review schema article with itemprop reviewRating, author, reviewBody User review structured data
Breadcrumb schema ol with itemprop itemListElement Navigation breadcrumb trail