Skip to main content
Published / updated

HTML Document Structure and Content

Before you start

You need: nothing. No web experience assumed.

You need installed: VS Code and a browser. This track uses VS Code rather than Visual Studio — it is what frontend work is done in, and the Live Server extension gives you instant reloads.

Time: about 45 minutes, plus the practice.

New to all of this? Start here explains what an application is made of and introduces the school system every example uses. Any word you do not recognise is in the glossary.

Learning objective

Write a valid HTML document from memory and choose the correct element for each piece of content.

Topics

  • The document skeleton
  • <head> and the metadata that matters
  • Headings and document outline
  • Text-level elements
  • Links and paths
  • Images, alt text and responsive sizing
  • Lists
  • Validating your HTML

The skeleton

<!DOCTYPE html>
<html lang="en-IN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Student Records — NexCoding Academy</title>
<meta name="description" content="Student records for NexCoding Academy.">
<link rel="stylesheet" href="/css/site.css">
</head>
<body>
<h1>Student Records</h1>
<p>Manage admissions, classes and contact details.</p>

<script src="/js/site.js" defer></script>
</body>
</html>

Four lines in that head are not optional.

<!DOCTYPE html> puts the browser in standards mode. Omit it and you get quirks mode, where the box model behaves as it did in 1998 and your CSS produces different results in different browsers.

lang="en-IN" tells screen readers which language to pronounce, and browsers which dictionary and date conventions to use. It costs nothing.

<meta charset="UTF-8"> must appear within the first 1024 bytes. Without it, non-Latin characters render as ä or ?. For a school system holding names in Telugu, Hindi or Tamil, this is the difference between working and not.

The viewport meta tag tells a mobile browser to use the device width. Omit it and the phone renders the page at 980px and zooms out, so your responsive CSS never activates. Every media query you write is dead without this line.

Script placement

<script src="/js/site.js"></script> <!-- blocks parsing -->
<script src="/js/site.js" defer></script> <!-- downloads in parallel, runs after parsing -->
<script src="/js/site.js" async></script> <!-- runs as soon as it downloads, order not guaranteed -->

Use defer for anything touching the DOM. The script downloads while the page parses and runs once the document is ready — so document.getElementById finds elements, and the page is not blocked while downloading.

async suits independent scripts such as analytics. Its execution order is not guaranteed, so never use it for scripts that depend on each other.

Headings

<h1>Student Records</h1>
<h2>Class 10 — Section A</h2>
<h3>Contact details</h3>
<h3>Fee status</h3>
<h2>Class 10 — Section B</h2>

Headings form the document's outline. Screen-reader users navigate by jumping between them, and search engines use them to understand structure.

Two rules:

  • One <h1> per page, naming what the page is.
  • Never skip levels. <h1> to <h3> leaves a gap that a screen reader reports as a missing section.

Headings are structure, not size. Choosing <h4> because it looks right is the most common HTML mistake there is — set the size in CSS and pick the heading by its place in the outline.

Text-level elements

<p>Ravi Kumar is in class 10, section A.</p>

<strong>Important:</strong> <!-- strong importance -->
<em>emphasised</em> <!-- stress emphasis -->
<b>bold</b> <!-- stylistically offset, no importance -->
<i>italic</i> <!-- alternate voice, e.g. a term -->

<code>SELECT * FROM Student</code>
<pre>preformatted, whitespace preserved</pre>
<abbr title="National Council of Educational Research and Training">NCERT</abbr>
<time datetime="2024-06-15">15 June 2024</time>
<small>Fees are non-refundable.</small>
<mark>highlighted</mark>
<br> <!-- line break within a block -->
<hr> <!-- thematic break -->

<strong> and <em> carry meaning; a screen reader changes intonation for them. <b> and <i> are purely visual. When the text genuinely matters, use <strong>.

<time datetime="2024-06-15"> gives machines an unambiguous date while showing humans whatever format you like. Useful for exam dates and due dates.

Never use <br> for spacing between paragraphs. Two paragraphs are two <p> elements; the gap belongs to CSS margin.

<a href="/students/list.html">All students</a>
<a href="students/list.html">Relative to this page</a>
<a href="../index.html">Up one directory</a>
<a href="#fee-summary">Jump to a section on this page</a>
<a href="https://nexcoding.in">Another site</a>

<a href="mailto:info@nexcoding.in">Email us</a>
<a href="tel:+919951510727">Call us</a>

<a href="/reports/attendance.pdf" download>Download attendance report</a>

<a href="https://nexcoding.in" target="_blank" rel="noopener noreferrer">Opens in a new tab</a>
PathResolves to
/students/list.htmlFrom the site root
students/list.htmlRelative to the current page
../index.htmlOne directory up
#section-idAn element on this page

rel="noopener noreferrer" is required with target="_blank". Without noopener, the opened page can manipulate your page through window.opener — a real phishing vector. Modern browsers imply it, but older ones do not, and being explicit costs nothing.

<!-- Useless: a screen reader lists "click here, click here, click here" -->
<a href="/students/list.html">Click here</a> to see students.

<!-- Useful -->
<a href="/students/list.html">View all students</a>

Screen-reader users often navigate by pulling up a list of every link on the page, stripped of surrounding text. Link text must make sense alone.

Images

<img src="/img/campus.jpg"
alt="NexCoding Academy main building"
width="800" height="450"
loading="lazy">

alt is required on every image, and its content depends on the image's purpose:

<!-- Meaningful image: describe what it conveys -->
<img src="/img/ravi-kumar.jpg" alt="Ravi Kumar, class 10 section A">

<!-- Decorative image: empty alt, so screen readers skip it -->
<img src="/img/divider.svg" alt="">

<!-- Never omit the attribute — a screen reader then reads the filename -->
<img src="/img/ravi-kumar.jpg">

An empty alt="" and a missing alt are different. Empty means "skip this"; missing means the screen reader falls back to announcing the file path.

width and height prevent layout shift. The browser reserves the space before the image loads, so content does not jump as images arrive. Give the intrinsic pixel dimensions and let CSS scale it:

img { max-width: 100%; height: auto; }

loading="lazy" defers images below the fold. Do not use it on an image visible on first paint — it delays the one thing the user is waiting for.

Responsive images

<img src="/img/campus-800.jpg"
srcset="/img/campus-400.jpg 400w,
/img/campus-800.jpg 800w,
/img/campus-1600.jpg 1600w"
sizes="(max-width: 600px) 100vw, 800px"
alt="NexCoding Academy main building">

srcset lists the available widths; sizes tells the browser how wide the image will render. The browser then picks the smallest file that will look sharp — which matters a great deal on a mobile connection.

<figure>
<img src="/img/results-chart.png" alt="Class 10 pass rates, 2020 to 2024">
<figcaption>Class 10 pass rates have risen from 78% to 94% since 2020.</figcaption>
</figure>

Use <figure> when the image needs a visible caption. Note the alt and the caption say different things — the caption is for everyone, the alt describes the image to someone who cannot see it.

Lists

<!-- Unordered: order does not matter -->
<ul>
<li>Mathematics</li>
<li>Science</li>
<li>English</li>
</ul>

<!-- Ordered: sequence is meaningful -->
<ol>
<li>Submit the admission form</li>
<li>Pay the first instalment</li>
<li>Collect the identity card</li>
</ol>

<!-- Description: name/value pairs -->
<dl>
<dt>Roll number</dt>
<dd>NCA-2024-0012</dd>

<dt>Class</dt>
<dd>10 — Section A</dd>

<dt>Parent contact</dt>
<dd>Suresh Kumar, 9951510727</dd>
</dl>

<dl> is the right element for a details panel — a set of labels and values. It is widely forgotten, and a <table> or a pile of <div>s is used instead.

Lists nest inside <li>, never directly inside <ul>:

<ul>
<li>Class 10
<ul>
<li>Section A</li>
<li>Section B</li>
</ul>
</li>
</ul>

Navigation is a list of links:

<nav aria-label="Main">
<ul>
<li><a href="/students/">Students</a></li>
<li><a href="/teachers/">Teachers</a></li>
<li><a href="/fees/">Fees</a></li>
</ul>
</nav>

That structure lets a screen reader announce "list of 3 items" — useful orientation that a row of bare <a> elements does not provide.

Validation

Run every page through the W3C validator. Browsers silently repair broken HTML, and they repair it differently — which is why a layout can be correct in Chrome and wrong in Safari.

The errors worth fixing first:

  • Unclosed elements
  • Elements nested where they are not permitted (a <div> inside a <p>)
  • Duplicate id values
  • Missing alt
  • Missing required attributes

Duplicate ids deserve special mention. document.getElementById returns only the first, and CSS #id styles only the first — so a form field silently stops working with no error anywhere.

Errors you will hit

What you seeCauseFix
Page renders as plain textWrong file extension, or opened as a file path oddlySave as .html; use Live Server
Changes do not appearBrowser cached the old fileCtrl+Shift+R, or DevTools → Disable cache
Everything after a point is boldAn unclosed tagCheck the Elements panel for the real nesting
Special characters show as boxesMissing <meta charset="utf-8">Add it in <head>
The page shows in DevTools but not the browserYou are looking at an element with display: noneCheck the Styles pane

View Source shows what the server sent; the Elements panel shows the live DOM. When they disagree, the difference is what your JavaScript did.

Common mistakes

  • Missing <!DOCTYPE html>, so the browser uses quirks mode
  • Missing the viewport meta tag, so responsive CSS never activates
  • Missing charset, so non-Latin names render as ?
  • Choosing headings by size rather than by outline position
  • Skipping heading levels
  • More than one <h1>
  • <br> for spacing between paragraphs
  • Missing alt, or alt on a decorative image that should be empty
  • No width and height, causing layout shift
  • loading="lazy" on an above-the-fold image
  • "Click here" link text
  • target="_blank" without rel="noopener"
  • Duplicate id values
  • Never validating

Practice

The course exercise is build a semantic form; this article covers the document it lives in.

  1. Write a complete student profile page from memory: doctype, lang, charset, viewport, title, description, one <h1>.
  2. Add a details panel using <dl> for roll number, class, date of birth and parent contact.
  3. Add a <figure> with a student photo, correct alt, explicit width and height, and a caption. Confirm the alt and caption say different things.
  4. Add navigation as a <nav> containing a <ul> of links.
  5. Add an ordered list of admission steps and an unordered list of subjects.
  6. Run the page through the W3C validator and fix every error.
  7. Remove the viewport meta tag, open the page on a phone or in device emulation, and record what happens.
  8. Remove charset and put a Telugu or Hindi name in the page. Record what renders.
  9. Give two elements the same id, then try to select the second with getElementById. Confirm you get the first.
  10. Add width and height to one image and omit them on another. Throttle the network in DevTools and watch which one shifts the layout.

Exercises 7 and 8 are the two that break a real site, and both are one missing line.

You can now

  • Write a valid HTML document from memory
  • Choose elements by meaning rather than appearance
  • Say what <head> is for and what belongs there
  • Nest elements correctly and find an unclosed tag
  • Tell View Source from the Elements panel

Review questions

  1. What breaks without the viewport meta tag?
  2. What is the difference between a missing alt and alt=""?
  3. Why should heading levels never be chosen for their size?
  4. Why do width and height on an image matter even when CSS resizes it?

Next: Tables and forms