Sitemap
Ade Code

Ade Code

8 min readOct 10, 2026

--

HTML

Semantic Landmark Elements vs. Non-Semantic Containers

Prior to HTML5, developers built web layouts using generic divisions (<div id="header">, <div class="nav">, <div id="sidebar">). While visually functional when styled with CSS, generic <div> elements convey zero meaning to search engine crawlers, screen readers, and other assistive technologies.

Get Ade Code’s stories in your inbox

Join Medium for free to get updates from this writer.

HTML5 introduced dedicated Semantic Landmark Elements that explicitly communicate the role of each page section.

Press enter or click to view image in full size

When to Use <div> and <span>

  • <div> (Block-level container): Use strictly for styling, visual wrappers, grid layouts, or positioning hooks where no inherent semantic meaning exists.
  • <span> (Inline-level container): Use for applying targeted CSS styles, scripts, or micro-markup to a specific fragment of text inside a paragraph or heading.

3. Structural Content Grouping: Section vs. Article

A common point of confusion in Web Development is choosing between <article> and <section>.

<!-- Example A: Sections inside an Article -->
<main>
<article>
<h1>The Science of Quantum Computing</h1>

<section aria-labelledby="heading-intro">
<h2 id="heading-intro">Introduction to Qubits</h2>
<p>Unlike classical bits...</p>
</section>

<section aria-labelledby="heading-entanglement">
<h2 id="heading-entanglement">Quantum Entanglement</h2>
<p>When particles become correlated...</p>
</section>
</article>
</main>

<!-- Example B: Articles inside a Section -->
<main>
<section aria-labelledby="latest-news">
<h2 id="latest-news">Department News</h2>

<article>
<h3>New Physics Lab Opening</h3>
<p>The university has inaugurated...</p>
</article>

<article>
<h3>Research Grant Awarded</h3>
<p>Dr. Smith received funding for...</p>
</article>
</section>
</main>

Decision Matrix

  • Ask yourself: “If I syndicate or publish this snippet on another website or RSS feed completely isolated from the rest of this page, does it make complete sense on its own?”
  • Yes $\rightarrow$ Use <article>.
  • No $\rightarrow$ If it is simply a chapter, sub-topic, or thematic grouping of a larger piece, use <section>.

4. Document Heading Hierarchy ($h1 \rightarrow h6$)

Headings construct an invisible outline of your page. Screen readers rely heavily on headings to let visually impaired users jump directly to relevant content blocks.

<!-- CORRECT: Logical Heading Structure -->
<h1>Computer Science Department</h1> <!-- 1 Primary Page Title -->
<h2>Undergraduate Programs</h2> <!-- Sub-topic Level 1 -->
<h3>B.S. in Computer Science</h3> <!-- Sub-topic Level 2 -->
<h3>B.S. in Software Eng</h3> <!-- Sub-topic Level 2 -->
<h2>Graduate Programs</h2> <!-- Sub-topic Level 1 -->
<h3>Ph.D. in AI</h3> <!-- Sub-topic Level 2 -->

<!-- INCORRECT: Broken Hierarchy -->
<h1>Computer Science Department</h1>
<h3>Undergraduate Programs</h3> <!-- BAD: Skipped h2 -->
<h5>B.S. in Computer Science</h5> <!-- BAD: Skipped h4 -->

Heading Rules

  1. Never skip levels going down. Do not transition directly from an <h1> to an <h3>.
  2. One <h1> per page (Recommended). While the HTML5 specification technically permits multiple <h1> elements if nested inside separate sectioning elements, web accessibility standards (WCAG) and SEO best practices strongly advocate for a single, authoritative <h1> defining the primary topic of the page.
  3. Do NOT choose heading tags for text size. Use CSS for font sizes, weights, and styling. Use HTML heading tags exclusively to define document relationships and structural depth.

5. Web Accessibility Standards (WCAG) & ARIA Landmarks

Creating a accessible web layout involves ensuring that users navigating with keyboards or screen readers can parse and move through your page as effectively as a sighted mouse user.

A. Skip Links (Keyboard Navigation)

Keyboard-only users press the Tab key to move through focusable elements (links, buttons, inputs). On pages with large navigation bars, forcing a user to press Tab 20 times before reaching the main content creates unnecessary friction. A Skip Link solves this problem.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Skip Link Implementation</title>
<style>
/* Visually hide the skip link until it receives keyboard focus */
.skip-link {
position: absolute;
top: -100px;
left: 10px;
background: #000000;
color: #ffffff;
padding: 10px;
z-index: 9999;
}
.skip-link:focus {
top: 10px; /* Moves into view on focus */
}
</style>
</head>
<body>

<!-- Must be the first focusable element inside <body> -->
<a href="#main-content" class="skip-link">Skip to main content</a>

<header>
<nav>
<!-- Complex site navigation links here -->
</nav>
</header>

<!-- Target of the skip link; tabindex="-1" allows JS/CSS focus shifting -->
<main id="main-content" tabindex="-1">
<h1>Main Dashboard Content</h1>
<p>Target content starts here.</p>
</main>

</body>
</html>

B. ARIA Landmarks and Accessible Naming

When multiple landmark elements of the same type exist on a single page (e.g., a main navigation and a footer navigation), assistive technologies require explicit labels to distinguish them.

<!-- Differentiating Navigation Landmarks -->
<header>
<nav aria-label="Global Site Navigation">
<ul>...</ul>
</nav>
</header>

<main>
...
</main>

<footer>
<nav aria-label="Legal & Policy Links">
<ul>...</ul>
</nav>
</footer>

<!-- Differentiating Sections via Heading Associations -->
<section aria-labelledby="department-announcements">
<h2 id="department-announcements">Announcements</h2>
<p>Content goes here...</p>
</section>

6. Comprehensive Practical Example

Below is a complete HTML5 template integrating semantic landmarks, accessible headings, keyboard navigation, and ARIA attributes without using any generic <div> layout wrappers.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>University Student Portal</title>
<style>
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: #00274c;
color: #ffffff;
padding: 8px 16px;
z-index: 1000;
text-decoration: none;
}
.skip-link:focus {
top: 0;
}
</style>
</head>
<body>

<!-- 1. Keyboard Accessibility Skip Link -->
<a href="#main-content" class="skip-link">Skip to main content</a>

<!-- 2. Global Site Header -->
<header>
<h1>State University Student Portal</h1>

<!-- Primary Navigation Landmark -->
<nav aria-label="Primary Navigation">
<ul>
<li><a href="/dashboard">Dashboard</a></li>
<li><a href="/courses">My Courses</a></li>
<li><a href="/grades">Grades & Records</a></li>
<li>
<button id="user-menu-button" aria-haspopup="true" aria-expanded="false" aria-controls="user-dropdown">
Account Options
</button>
<ul id="user-dropdown" aria-labelledby="user-menu-button" hidden>
<li><a href="/profile">Profile Settings</a></li>
<li><a href="/security">Security</a></li>
<li><a href="/logout">Sign Out</a></li>
</ul>
</li>
</ul>
</nav>
</header>

<!-- 3. Tangential Content / Sidebar Landmark -->
<aside aria-label="Quick Tools and Utilities">
<h2>Student Resources</h2>
<ul>
<li><a href="/library">University Library</a></li>
<li><a href="/calendar">Academic Calendar</a></li>
<li><a href="/helpdesk">IT Support</a></li>
</ul>
</aside>

<!-- 4. Primary Page Content Landmark -->
<main id="main-content" tabindex="-1">
<article>
<h2>Autumn Semester Overview</h2>
<p>Welcome back! Below are your active modules and administrative notices.</p>

<!-- Section A: Courses -->
<section aria-labelledby="enrolled-courses-heading">
<h3 id="enrolled-courses-heading">Enrolled Modules</h3>
<ul>
<li><strong>CS301:</strong> Advanced Database Systems</li>
<li><strong>CS305:</strong> Web Architecture & Development</li>
<li><strong>MATH201:</strong> Linear Algebra II</li>
</ul>
</section>

<!-- Section B: Deadlines -->
<section aria-labelledby="deadlines-heading">
<h3 id="deadlines-heading">Upcoming Deliverables</h3>
<ul>
<li>CS305 Page Layout Assignment — Due October 20</li>
<li>MATH201 Problem Set 4 — Due October 24</li>
</ul>
</section>
</article>
</main>

<!-- 5. Global Site Footer -->
<footer>
<p>&copy; 2026 State University. All rights reserved.</p>
<nav aria-label="Footer Legal Links">
<ul>
<li><a href="/privacy">Privacy Statement</a></li>
<li><a href="/accessibility">Accessibility Commitment</a></li>
</ul>
</nav>
</footer>

</body>
</html>

Master Class: Accessible HTML Forms & Native Constraint Validation

Designing web forms that comply with the Web Content Accessibility Guidelines (WCAG 2.2) requires strict structural semantics, explicit programmatic associations, clear visual boundaries, and non-reliance on JavaScript for core validation rules.

1. Explicit Programmatic Labeling

Every form control must have a programmatically linked label so screen readers announce the control’s name, purpose, and type when focused.

Explicit for / id Association

The primary method for associating a label with a control is linking the <label> element's for attribute to the input's id attribute.

<!-- Correct: Explicit Association -->
<div class="form-group">
<label for="user-email">Email Address:</label>
<input type="email" id="user-email" name="email">
</div>

Common Anti-Patterns

  • Using placeholder as a label: Placeholders disappear when text is entered, have low default contrast ratios, and are omitted by many assistive technologies.
  • Implicit nesting without for: While wrapping an <input> inside a <label> is valid HTML5, some older screen reader and browser combinations handle explicit for/id bindings far more consistently.

2. Structural Control Grouping (<fieldset> and <legend>)

When related inputs share a collective context (e.g., radio button options, multi-select checkboxes, or address sub-fields), they must be grouped using a <fieldset> with a descriptive <legend>.

<fieldset>
<legend>Preferred Contact Method</legend>

<div>
<input type="radio" id="contact-email" name="contactMethod" value="email">
<label for="contact-email">Email</label>
</div>

<div>
<input type="radio" id="contact-phone" name="contactMethod" value="phone">
<label for="contact-phone">Phone Call</label>
</div>
</fieldset>

Why <legend> is Crucial for WCAG

When a visually impaired user tabs between radio buttons or checkboxes, a screen reader reads both the <legend> title and the individual <label> text. Without <fieldset> and <legend>, the user hears only "Email" or "Phone Call" without context on what choice they are making.

3. Native HTML5 Constraint Validation Attributes

Native HTML5 validation handles form input rules at the browser level before submission, reducing client-side JavaScript overhead.

Press enter or click to view image in full size

Complex Regex Validation Example

<div class="form-group">
<label for="course-code">Course Code:</label>
<input
type="text"
id="course-code"
name="courseCode"
required
pattern="[A-Z]{3}-\d{3}"
placeholder="e.g., CSX-101"
aria-describedby="code-instructions"
>
<span id="code-instructions" class="field-help">Format: 3 uppercase letters, a hyphen, and 3 digits (e.g., CSX-101).</span>
</div>

4. Accessible Error Messaging & WCAG Attributes

WCAG 2.2 Success Criteria 3.3.1 (Error Identification) and 3.3.2 (Labels or Instructions) require that error messages and help texts are programmatically connected to their corresponding inputs.

Key ARIA Form Attributes

  • aria-describedby: Binds input elements to secondary explanatory text or error messages via element IDs.
  • aria-invalid: Communicates whether the current field value violates validation rules (aria-invalid="true").
  • aria-required: Explicitly declares mandatory fields for screen readers (complements native required).
  • aria-live: Announces dynamically injected error messages as soon as they appear.
<!-- Input with Accessible Help & Error Markup -->
<div class="form-group">
<label for="account-num">Account Number:</label>
<input
type="text"
id="account-num"
name="accountNum"
required
aria-required="true"
aria-invalid="true"
aria-describedby="num-help num-error"
>

<span id="num-help" class="field-help">Your 8-digit university account identifier.</span>

<!-- Dynamically injected or conditional error message -->
<span id="num-error" class="field-error" role="alert">
Error: Account number must contain exactly 8 digits.
</span>
</div>

5. Comprehensive WCAG-Compliant Form Example

This production-ready registration form combines explicit labels, control grouping, constraint validation, accessible help text, and ARIA relationships.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Accessible Enrollment Form</title>
<style>
.form-group {
margin-bottom: 1.25rem;
}
label {
display: block;
font-weight: bold;
margin-bottom: 0.25rem;
}
.field-help {
display: block;
font-size: 0.875rem;
color: #4a5568;
margin-top: 0.25rem;
}
.field-error {
display: block;
font-size: 0.875rem;
color: #c53030;
font-weight: bold;
margin-top: 0.25rem;
}
input:invalid:focus {
outline: 2px solid #c53030;
}
</style>
</head>
<body>

<main>
<h1>University Course Enrollment</h1>

<form action="/api/enroll" method="post" enctype="multipart/form-data" novalidate>

<!-- Personal Details Section -->
<fieldset>
<legend>Student Profile</legend>

<!-- Name Input -->
<div class="form-group">
<label for="full-name">Full Name <span aria-hidden="true">*</span></label>
<input
type="text"
id="full-name"
name="fullName"
required
pattern="[A-Za-z\s']{2,50}"
autocomplete="name"
aria-describedby="name-help"
>
<span id="name-help" class="field-help">Must be 2–50 characters long (letters and spaces only).</span>
</div>

<!-- Student ID -->
<div class="form-group">
<label for="student-id">Student Identification Number <span aria-hidden="true">*</span></label>
<input
type="text"
id="student-id"
name="studentId"
required
pattern="STU-\d{5}"
placeholder="STU-12345"
aria-describedby="id-help"
>
<span id="id-help" class="field-help">Format: Capital letters "STU-" followed by 5 digits.</span>
</div>
</fieldset>

<!-- Program & Files Section -->
<fieldset>
<legend>Academic Selection</legend>

<!-- Major Selection Dropdown with Option Groups -->
<div class="form-group">
<label for="faculty-select">Faculty & Major <span aria-hidden="true">*</span></label>
<select id="faculty-select" name="faculty" required>
<option value="">-- Choose an Academic Program --</option>
<optgroup label="Faculty of Science">
<option value="cs">Computer Science</option>
<option value="math">Mathematics</option>
<option value="phys">Physics</option>
</optgroup>
<optgroup label="Faculty of Arts">
<option value="hist">History</option>
<option value="phil">Philosophy</option>
</optgroup>
</select>
</div>

<!-- File Upload -->
<div class="form-group">
<label for="transcript-upload">Official Transcript (.PDF or .DOCX) <span aria-hidden="true">*</span></label>
<input
type="file"
id="transcript-upload"
name="transcript"
required
accept=".pdf,.docx"
aria-describedby="file-help"
>
<span id="file-help" class="field-help">Maximum upload file size: 5MB.</span>
</div>
</fieldset>

<button type="submit">Submit Enrollment Request</button>
</form>
</main>

</body>
</html>

--

--