5. Semantic HTML#
As explained in previous chapters, the purpose of HTML markup is to semantically annotate text. When we say that HTML is “semantic”, we mean that it is used to describe the meaning, structure, or purpose of text - not to describe that content’s visual appearance. Using HTML only for semantics helps follow the separation of concerns principle (making your code more flexible and easier to modify and maintain). It also helps to make web pages more accessible, as they can be better understood and navigated by assistive technology such as screen readers.
Un-semantic HTML is HTML that doesn’t actually describe the meaning of some content but may have a visual appearance that suggests that meaning through CSS. For example, an <h1> element is used to indicate a top-level heading, such as the page’s title. But by default, browsers will give <h1> elements a different visual appearance than unmarked content (usually by making it larger and bold). It is possible to achieve a similar visual effect just through CSS:
<!-- HTML -->
<h1>This is a real heading!</h1>
<p class="fake-header">
This just LOOKS like a heading.
</p>
/* CSS */
.fake-header {
font-size: 2em;
font-weight: bold;
}

In this example, the second line looks like a heading (it’s big and bold) - but the HTML says that it isn’t a heading - it’s just a paragraph. This can cause problems and confusion, especially for screen readers and other systems that are not concerned with visual appearance. A blind user visiting this page wouldn’t know that the second paragraph is supposed to act as a heading because that isn’t indicated in the HTML (just by the rendered visual appearance).
For this reason, it’s vitally important that HTML markup is only used to provide accurate semantic annotation. Don’t use an un-semantic element type (such as a heading) because of its default appearance. Do use elements that provide additional information about the purpose of content when possible so that screen readers and other assistive technology will know about that.
Note
Many of the rules in the code style guide exist to ensure that HTML is used semantically.
This chapter discusses many specific HTML elements, providing details on including them in your documents and how to use them semantically to ensure your page best supports assistive technologies. It also discusses the ARIA standard for adding additional semantic meanings to elements to further support accessibility.
Syntax vs Semantics
Syntax and semantics are two fundamental concepts in programming and markup languages, and understanding their differences is key to effective coding, especially in languages like HTML.
Syntax refers to the rules that define the structure and arrangement of code—how code should be written to be considered valid by a browser or interpreter. In HTML, syntax is about using tags correctly, including:
Properly opening and closing tags (e.g.,
<p>Text</p>)Nesting tags correctly (e.g.,
<div><p>Text</p></div>)Using attributes appropriately within tags (e.g.,
<img src="image.jpg" alt="Image description">)
Semantics refers to the meaning behind the code—the intent or purpose of different tags and elements. In HTML, using semantic elements improves the clarity of the document structure for both developers and browsers. Semantic HTML also enhances accessibility and SEO (Search Engine Optimization) by helping screen readers and search engines understand the content better.
5.1. Specific HTML Elements#
There are over 100 different HTML elements, each with its own semantic meaning. This chapter is not intended to be a complete reference (use the MDN element reference for that!), but rather to provide specific points and requirements to use them effectively and semantically.
5.1.1. Hyperlinks#
Hyperlinks are fundamental to HTML (it’s right in the name!), so the anchor element <a> may be the most important. The <a> element denotes a hyperlink, text that when activated (e.g., clicked or selected), will cause the browser to navigate to a different URL than the current one. Clicking on a hyperlink is called following the link.
Tip
From a usability perspective, make the destination of your hyperlinks unambiguous to your viewers - don’t make them guess the destination!
As discussed in HTML Fundamentals, the <a> element almost always has an href attribute that indicates where the text of the element goes to:
<!-- a external hyperlink to `https://duke.edu` -->
<a href="https://duke.edu">Duke University</a>
The value of the href attribute can be any valid URL (including ones that use protocols other than https). Most a hyperlink will have one of two types of URLs:
external hyperlinks are hyperlinks that navigate to a different domain. For example, a link to Google or the Duke homepage (such as the above example) from this book would be an external hyperlink because the book’s pages aren’t on the same domain. External hyperlinks are usually written as full URLs, including the protocol since they include the other domain they link.
internal hyperlinks are hyperlinks that navigate to other web pages in the same domain. For example, links to other chapters in this book are internal hyperlinks. The URLs for these hyperlinks are just specified as just the path portion of the other URLs; the domain and protocol are assumed to be the same and so ommitted as redundant. For example:
<!-- An internal hyperlink to a different `.html` file in the same domain --> <a href="/client-side-development.html">Chapter 2: Client-Side Development</a>
Internal hyperlinks may use absolute paths as in the above example. The URL path is then determined from the “root folder” of the server. Such URLs will still resolve even if the link is on a page nested inside one or more folders on the server. It is also possible for internal hyperlinks to use relative paths, in which case the target of the hyperlink is relative to the URL of the current page (the file being rendered that includes the link):
<!-- An internal hyperlink to a file in a different folder on the same domain. Note that the relative URL can include moving up a folder. --> <a href="../assignments/exercise-01.html">Exercise 01</a>
As noted in HTML Fundamentals, a hyperlink’s URL can also include the fragment (the part after the
#) to create a bookmark link to an element with a particularidattribute on a page:<a href="index.html#nav">Link to element on `index.html` with `id="nav"`</a> <a href="#title">Link to element on current page with `id="title"`</a>
To open a hyperlink in a new tab or window (browser-dependent), provide a target="_blank" attribute. The "_blank" value is a special keyword that tells the browser to open the link in a new context.
Finally, it is important to note that activating a hyperlink (such as by clicking on it) sends a new HTTP request to the server, which then responds with a new page for the browser to render. This means that even if you have a hyperlink to the same page (except for bookmark links), the link will cause the page to “reload”:
<!-- in index.html -->
<a href="index.html">A link to this same URL will cause the page to reload</a>
Reloading a page means that all of the assets (images, etc) may be re-downloaded, and any state information in JavaScript will be lost. Be careful about when you trigger page reloads.
Stateless Protocol
HTTP is a stateless protocol. That means that every single HTTP request - every page load - is considered to be independent and has no inherent relationship. Visiting or loading a page once will not impact future page loads; every visit is a “first and last time”. Servers and browsers can and often do include extra logic to help track sessions to make it seem like page loads are related (e.g., so that items in an online shopping cart stay around), but this is extra work separate from the page encoding itself. In client-side development, it is best to act as if each page load were a unique one-time occurrence unless the server informs otherwise.
Hyperlinks are for navigation - activating a hyperlink directs the user to a different URL. The <a> element should not used for clickable elements on a
page that do not perform navigation (such as submitting a form or expanding a dropdown). Clickable non-navigation should be annotated as <button> elements.
Similarly, do not use a <button> for navigation - that should be a hyperlink <a>! If you want a hyperlink to look like a button, you can use CSS to style
it in that way - Bootstrap provides support for doing this easily for example.
5.1.1.1. Accessible Hyperlinks#
Assistive technologies such as screen readers can jump between hyperlinks on a page quickly; this helps users understand what navigation elements are available and quickly get around without needing to listen to extra content. For this reason, to make a hyperlink accessible, the text content of a link should indicate where it goes even when taken out of context:
<!-- Do this -->
<a href="/assignments">see assignments here</a>
<!-- Don't do this -->
see assignments <a href="/assignments">here</a>
In the second example, the link only consists of the word “here”, meaning that when a screen reader lists all of the navigational elements, it will just list the word “here” without any context for the user to understand what is there! This will also help sighted users, who are more easily able to see and understand the links in context.
Similarly, you’ll want to make sure that “similar” link text that refers to different locations is unique:
<!-- Don't do this; screen readers can't distinguish between the links -->
For the first <a href="exercise-01.html">exercise</a> you'll practice HTML, for
the second <a href="exercise-02.html">exercise</a> you'll practice CSS.
Using ARIA attributes such as aria-label (see below) can also help ensure that hyperlink text is accessible by providing screen-reading specific context if you struggle to make the prose itself self-contained.
5.1.2. Inline Textual Elements#
Historically individual words of a sentence can be typo-graphically formatted to be marked as distinct by making them either boldface or italicized. But these are appearance/formatting properties only - they don’t provide any semantic information about the text. So while HTML includes elements <b> for italics and <i> for italics, these are historical artifacts and should not be used. For example, the <b> causes the browser to render text as bold but doesn’t do anything to indicate why that text is bold. A sighted user may be able to infer something about the boldness from context, but a screen reader can make no such guesses from such un-semantic elements.
HTML instead includes several inline textual elements that you can use to set apart text semantically. By default, these may cause the text to render in boldface or italics (and that can be further customized with CSS) while providing additional semantic support for the text to make it more accessible to assistive technologies.
Some of the more useful or interesting inline semantic elements are described below, along with links to their documentation in MDN for further information:
<em>marks text as emphasized, which by default is italicized.<strong>marks text as strongly important, which by default is boldface. This is distinct from<em>emphasis, as it’s not just stressed by particularly important.<code>marks text as computer code, which by default is inmonospace. The<pre>element can be used to manage pre-formatted text to help with larger code blocks.<del>marks text as deleted, which by default is strike-through.<u>marks text as having a non-textual annotation, such as an underline or “energy lines” or some other decoration. While by default, such text renders with an underline, the meaning of this element is to annotate that element has having something like an underline (even if that underline is provided by the tag!) Overall, it is best to avoid this element as mostly un-semantic (similar to how you avoid<i>and<b>), and use the CSStext-decorationproperty to create underlines instead.<cite>marks text as being the title of a cited creative work - like the title of a book or movie. Note that this doesn’t mark what you may think of as a “citation” in an essay - it isn’t the inline(Brown, 1999)from MLA-style or an item in a bibliography - it just marks the title of a work. By default, this renders text italicized.<abbr>marks text as being an abbreviation or acronym. In particular, if you give the element atitleattribute that is the expanded meaning of the acronym, the user will be able to hover over that element and see a pop-up of the meaning:<abbr title="Hypertext Markup Language">HTML</abbr>
This helps with providing definitions outside the flow of the text - though when presenting information, it’s best to include that definition as part of the sentence!
<time>represents a time or date. Providing thedatetimeattribute can encode the time in machine-readable format, which can help with search engine optimization or other systems:<time datetime="2023-12-23">New Year's Eve</time>
Importantly, although many of these elements provide some default styling, you should only use them for semantic purposes. Don’t use the <em> element just because you want the text to be italic; instead, you could use another semantic element (or a <span>, see below) and then use CSS to style that element: the font-style property can make content italic, the font-weight property can make it bold, the text-decoration property can add underlines, etc.
Historical Note
The <i> (italics) and <b> (bold) elements were originally part of HTML as purely styling elements without semantic meanings (and thus should not be used). Modern HTML does assign semantic meaning to these elements: <i> is used for content that is “set apart” (but for a reason covered by another element), and <b> is used for content that “deserves attention” (but for a reason not covered by another element). Because the meaning of these elements is vague, it’s often best not to use them to avoid confusion. If you want to make some text italic, think about why: is it because it is being emphasized (so it should be <em>) or because it is a term that needs to be defined or a work being cited? A better, more informative element that you can use to annotate content almost always exists.
5.1.3. Images and Media#
Although webpages are primarily a textual medium, they often contain significant amounts of purely visual information. This includes media such as images or video, but also visual labels such as icons (e.g., a “search” button labeled with a magnifying glass or a hamburger button). These features can be created through multiple elements, though specific care is needed to ensure they are accessible and can be perceived by screen readers.
The most common form of visual media on the web are images, created with the <img> element. As noted in HTML Fundamentals,
the src attribute specifies the path to the location of the image file that will be displayed. Most commonly, this will be a relative path to a file
served from the same domain (e.g., src="imgs/my_pic.jpg"). But it is also possible to use an absolute path to an image location on another domain
(e.g., src="http://pictures.com/cat_pic.jpg") - this is known as hotlinking an image. Hotlinking is often
considered bad practice - not only does it cause another domain to provide download bandwidth for content on your site, but it also means that the file being served may change without your knowledge, causing your site to include undesired content accidentally.
To make images accessible, always include an alt attribute that gives alternate text for when the image cannot be displayed (e.g., on-screen readers, but also if the image fails to load):
<img src="puppy_picture.jpg" alt="a cute puppy">
Screen readers will read this element as “a cute puppy, image”. Note that the “alt-text” should not include introductory text such as “a picture of”, as screen readers will already report that something is an image! The WAI (Web Accessibility Initiative) provides further guidance on writing effective alt text.
For more complex images (such as charts or infographics), you can additionally provide a hyperlink to a longer description by using the longdesc attribute. This attribute takes a value that is a URI (relative or absolute; what you would put in the href attribute of a hyperlink) referring to where the description can be found. Screen readers will prompt the user with the option to navigate to this long description.
<img src="accessibility_infographic.png"
alt="an infographic showing how to make web pages accessible"
longdesc="infographic_text.html"> <!-- link to other page with text description -->
Of course, providing descriptive text for images can be useful for all users (so they know what they are looking at)! You can semantically add a caption to an image by placing the image inside a <figure> element and then using a <figcaption> element for the caption:
<figure>
<img src="chart.png" alt="a chart showing some information">
<figcaption>
A caption for the above figure. It provides the same information
but in a text format.
</figcaption>
</figure>
The <figure> and <figcaption> elements are good examples of semantic elements used together: a <figcaption> can only be used inside of a <figure> (and must be the first or last child of that element!). If you wish to adjust the appearance or placement of the caption, you would use CSS.
It is also possible to embed video or audio elements into a webpage by using the <video> and <audio> elements respectively. These elements expect a “source”, specified either with the src attribute or <source> child element (used to have multiple simultaneous sources):
<!-- basic example of <video> element, adapted from MDN -->
<video controls width="400">
<source src="media/videos/cat-video.mp4" type="video/mp4" />
<!-- text to show if video isn't supported -->
Video not supported. <a href="/media/videos/cat-video.mp4">download the video</a>
</video>
Both <video> and <audio> support a large number of additional attributes (such as controls in the above example) that are used to customize the presentation, controls, and playback. Full discussion of all these options is outside the scope of this text; see the appropriate documentation for more details and examples.
Note that video controls, in particular may not be fully accessible to screen readers depending on the browser. At the very least, you should add captions and subtitles to videos (by providing <track> child elements) in to make sure all users can access them.
Finally, the <canvas> element is used for programmatically generating graphical images (2d or 3d) using JavaScript, It is the HTML basis for many graphical interactions or web games - though most of the actual work is done in JavaScript and other languages.
5.1.4. Headings#
Heading elements (<h1>, <h2>, through <h6>) are used to provide structure to a web page, allowing users and screen readers to navigate and move through different parts of the content easily. <h#> elements are block-level elements that are by default larger and bolder than normal text (with smaller headings rendered as smaller). However, do not use heading elements only to change the formatting of text - use CSS for that!
Warning
Be careful not to confuse a heading (<h1>) element with the <head> element (used for meta-data), or wth the <header> sectioning element described below!
Browsers and screen readers use heading elements to automatically construct a “table of contents” for the page. Think of them similar to chapter or section titles - how you would label parts of an “outline” of the page. Each web page should only have a single <h1>, representing the “title” of the page. Different sections may be marked by <h2> section titles, while subsections would be marked by <h3> subsection titles and so on. A page may have many <h2> or <h3> elements, each marking a “level” in the outline. For example, the sections of this chapter are annotated with <h#> headings:
<h1>Semantic HTML</h1>
<h2>Specific HTML Elements</h2>
<h3>Hyperlinks</h3>
<h3>Inline Textual Elements</h3>
<h3>Images and Media</h3>
...
<h2>ARIA for Accessibility</h2>
Pages may not have any sections or subsections and thus would not have any <h2> or smaller elements.
Because screen readers and other systems will use headings to generate a “table of contents automatically” (which helps screen readers quickly navigate the page), headings must be both meaningful and hierarchical. They should be meaningful in describing what content is in the section they start, similar to a book section title. They should be hierarchical in that they don’t skip levels: every <h3> has an <h2> above it - you can’t have a sub-section that isn’t in a section!
See also Sectioning Elements below for more about the structural organization of page content.
5.1.5. Non-Semantic Elements#
While the purpose of HTML is to provide semantic content information, some elements have no specific meaning or appearance. The <div> (division) element is used to “group” other content together, providing a shared parent or container for that content. It is a block element that has no additional semantic or appearance impact on its own; it is just used to organize content within the tree of HTML elements.
<!-- these three paragraphs will all have the same
"parent" which can be styled on its own -->
<div>
<p>Lorem ipsum dolor</p>
<p>sit amet consectetur</p>
<p>Adipisicing elit</p>
</div>
<!-- these paragraphs are in a different "group" -->
<div>
<p>Lorem ipsum dolor</p>
<p>sit amet consectetur</p>
</div>
This is particularly for organizing content and for applying styling with CSS:
<!-- A collection of "cards" on a page -->
<div class="card-group">
<div class="card">
<div class="card-header">Featured</div>
<div class="card-body">
<h2>Card 1</h2>
<p>An information card</p>
</div>
</div>
<div class="card">
<div class="card-body">
<h2>Card 2</h2>
<p>Another information card</p>
</div>
</div>
</div>
In the above example, <div> elements are given different CSS classes to style them differently: the elements with the card class may be styled to look like a “card” of content, while the elements with card-header may be styled to look like the headers of those cards. As in this example, it is very common to have multiple nested <div> elements (a <div> inside of a <div> inside of a <div>) - indeed, <div> is possibly the most common HTML element used in the web!
Remember that a <div> provides no semantic information; any styling changes applied through it will only be perceivable by sighted users! Thus it is always good to consider whether another block element - such as a paragraph, a heading, or a sectioning element - may be more appropriate (and they may not be).
Similarly, a <span> element is like a <div> (it has no semantic or appearance impact on its own) but is an inline element so it goes inside of text content to mark short passages:
<p>Baa baa <span class="dark">black</span> sheep,
have you any wool?</p>
A <span> can apply styling to inline content, such as to make a certain work a different color. However, because there is usually a reason for such styling changes, it is almost always better to use an appropriate semantic textual element instead. For example, if the word is important, use an <em> instead. You can think of <span> as the element when inline styling is needed, but no other element is appropriate.
5.1.6. Sectioning Elements#
The <h#> heading elements are part of the original HTML specification and so will be supported by all screen reader systems. However, HTML 5 introduced additional elements that can be used to help organize web page content to make its structure more explicit. These are often referred to as sectioning elements. These are all block-level elements that have no appearance impact on their own but do provide semantic information about the organization of their content. You can think of these as specialized <div> elements - they group elements together, but also provide some meaning as to why they are grouped!
Note
Many consider these section elements to be “semantic” HTML, while this guide takes a more encompassing view of HTML elements and their role in semantics.
These elements are often referred to as semantic elements or “sectioning elements”. These are all block-level elements that produce no visual effects on their own but provide semantic structuring to web content. You can think of them as specialized <div> elements (or “named” <div> elements).
Common sectioning elements are described below.
A
<section>represents a specific section of content. In particular, a “section” is a portion of content that would be identified by a heading (e.g., an<h2>or an<h3>) - a section should almost always contain a heading element as a child. Thus, this element can be used to group all of the content of that section (which falls below that heading) semantically. If a group of content would not have a heading, it probably isn’t a<section>(and would likely just be represented by a<div>).<section> <h2>HTML</h2> <p>...</p> <section> <h3>Introduction</h3> <p>....</p> </section> <section> <h3>Syntax</h3> <p>....</p> </section> </section>
Note that as in the above example,
<section>elements can contain other<section>elements (“subsections”), each with an appropriate heading.Remember that a
<section>has no default appearance, though you can use CSS to customize the elements. They are used to provide organizational semantics that screen readers and other systems can use.The
<section>is the most generic of the sectioning elements; other sectioning elements can be seen as “more specific” types of sections.A
<header>represents introductory content, such as heading elements, titles, or banner images. It may also include common page elements such as navigation or search bars.
“Head” Confusion
Be careful not to confuse a <header> with a heading (<h1>) element or the <head> element. A <header> is a sectioning element for introductory content shown on the page and can contain multiple child elements. The <head> is an element that is NOT part of the <body> (so is not shown in the web page), and contains metadata about that page.
A page almost always should contain a <header> element that has introductory content for that page, such as the <h1> and banner images. This usage implies the role="banner" landmark role (see ARIA for Accessibility below). In addition, specific <section> elements may include a <header> to contain the introductory content for that section.
<body>
<!-- this is the header ("banner") for the entire page -->
<header>
<h1>Page Title</h1>
</header>
<section>
<!-- this is the header for this section -->
<header>
<h2>About Section<h2>
</header>
</section>
</body>
While it’s possible to provide a <header> for every section (and every <section> should have a heading), in practice, a <section> with limited introductory
content - such as just a single heading element - won’t have a separate <header> defined since it doesn’t provide a lot of additional information about the
page structure. It can be helpful but is not required.
A
<footer>usually contains information about the author or source of the page’s content, including copyright information. This element implies therole="contentinfo"landmark role (see ARIA for Accessibility below).A
<footer>still contains visible page content, so it goes inside the<body>element! Typically pages will have a single<footer>element - this is placed at the bottom of the page as the last element of the<body>, but not always (using this sectioning element can help with page navigation if the<footer>is elsewhere!) The<footer>contains page-visible information about the content, such as the author and copyright information. It does not contain the “conclusion” section of a page (that would be a normal<section>)! Author information is often placed within an<address>element, which represents contact information.<footer> Created by <address><a href="mailto:joel@uw.edu">joel@uw.edu</a></address>. © 2024 </footer>
Individual
<section>elements can also have their own<footer>. However, this practice rarely occurs as sections typically do not have authors different from those on the rest of the page. It is more common for an<article>element to have its own<footer>.The
<main>element represents the “main” or central content of the document. A web page can only have a single<main>element. This element implies therole="main"landmark role (see ARIA for Accessibility below).Most all pages will have a
<main>element. It usually comes after the<header>(but not inside - in fact,<main>cannot be a descendant of<header>). The<main>element will then contain any<section>elements of the page, and be followed by the<footer>:<body> <header> introductory content </header> <main> ...main content <section> ...section content </section> ... more content (including more sections) </main> <footer> author and copyright information </footer> </body>
The above is a typical structure for an HTML document.
A
<nav>represents the part of a page that provides navigation links, such as a navigation bar or table of contents. Not all links need to be in a<nav>; this element represents a section of the webpage that is purely navigational (think of it as a specialized<section>). This element implies therole="navigation"landmark role (see ARIA for Accessibility below). A<nav>element will often be found in the<header>(for the nav bar at the top of the page), though that is not required - a page may have multiple<nav>sections.An
<article>is similar to a<section>, but represents standalone content
that could be published independently - such as a news article or a blog post. If a section would likely be published as its own document, then it may be an<article>. Note that a<section>may group together multiple<article>elements (such as a blog roll), and an<article>might contain more than one<section>. This mirrors how a newspaper is structured (e.g., with a “Sports Section” that contains articles, which may themselves have different sections).
Overall, using semantic sectioning elements can help make the content clearly structured, as well as organize the content for screen readers and make it more accessible for users to navigate the page. Most every page should have a <header>, <main>, and <footer>; pages with more content will be broken up into <section> elements; and sites with multiple pages will organization navigation through a <nav>.
5.1.7. Lists#
Lists in HTML help structure content as grouped, related items. Semantic HTML uses <ul> (unordered list) and <ol> (ordered list) elements to convey meaning. An <ul> is used for items without a specific sequence, like feature lists, while an <ol> is ideal for ordered steps or rankings. Inside these lists, <li> (list item) tags contain individual items. Using semantic lists improves accessibility, making screen readers identify and interpret the content structure more effectively.
An unordered list is useful for non-sequential items, like key features of an investment portfolio:
<ul>
<li>Diversification across multiple sectors</li>
<li>Focus on long-term growth</li>
<li>Low management fees</li>
</ul>
Ordered lists communicate a specific sequence, such as steps for calculating return on investment (ROI):
<ol>
<li>Determine initial investment</li>
<li>Calculate net profit
<ol>
<li>Subtract initial investment from total earnings</li>
<li>Adjust for any additional expenses</li>
</ol>
</li>
<li>Divide net profit by initial investment</li>
<li>Multiply by 100 to get ROI percentage</li>
</ol>
Notice how we can include sublists as an item in the list.
5.1.8. Tables#
The <table> element represents tabular data, such as for a data table like you might find in a spreadsheet program. Tables are one of the more complex structures in HTML, requiring several different elements and attributes. This section provides a brief example of making simple and accessible tables; for more examples and guidance see MDN’s documentation for the <table> element as well as the MDN’s Learn HTML tables guide.
In HTML, a table is made up of rows, each containing a number of cells. Corresponding cells across rows imply a column, though such columns are not explicitly noted in HTML.

Tables are encoded in HTML using the <table> element. A <table> contains multiple <tr> (table row) elements representing the rows of the data. Each <tr> row contains multiple <td> (table datum) elements representing the individual data cells.
| Ada | 64 | 135 |
| Bob | 74 | 156 |

A <th> (table header) element represents a cell that is in the “header” row or column, can is used in place of a <td> (it is also a cell, just a specialized kind of cell). For example, the first row a table might contain <th> element instead of <td> elements because those cells are the headings for their respective columns. By default, <th> elements will render their content in bold type. A <th> element expects a scope attribute indicating whether it is a header for a "row" or for a "col" (column). While the scope may be redundant for simple tables, it is best practice to include it to ensure that assistive technologies like screen readers best support presenting and navigating the table.
<table>
<tr>
<th scope="col">name</th> <!-- header cell -->
<th scope="col">height</th>
<th scope="col">weight</th>
</tr>
<tr>
<td>Ada</td>
<td>64</td>
<td>135</td>
</tr>
</table>
Tables can also be made more accessible by including table sectioning elements (similar in concept to the sectioning elements <header>, <main>, and <footer>). The <thead> element groups rows that are the head of the table with information about the columns (usually filled with <th> cells); the <tbody> element groups rows that include the main body of the table, and the <tfoot> element groups rows that are at the bottom of the table with information about the columns (e.g., summary data). These elements have no appearance impact but do help with organizing and presenting the table. They are also the closest to using a <div> to style different table sections!
<table>
<thead> <!-- the "header" section -->
<tr>
<th>name</th>
<th>height</th>
<th>weight</th>
</tr>
</thead>
<tbody> <!-- the "main" section -->
<tr>
<td>Ada</td>
<td>64</td>
<td>135</td>
</tr>
</tbody>
</table>
Cell elements (<td> or <tr>) can be made to stretch across multiple rows or columns by specifying the colspan and rowspan attributes. If one cell stretches across multiple columns or rows, omit the <td> cells it spans. Note that spanning rows or columns is not common in tabular data (since a datum won’t span multiple features or multiple observations) and can make it difficult for people using screen readers to understand the data structure. The better practice is to organize the data so that it doesn’t need to do such spanning.
As with any other elements, tables are primarily styled using CSS; HTML attributes should only be used for the structure of the table. Note that CSS rules will usually apply to the cells (<td>, <th>) in a table and not to the rows or sections. Unfortunately, many properties are not inherited from rows; those properties need to be applied directly to the cells. Some specific CSS properties apply only to table elements, such as border-collapse and border-spacing (which are used to style the spacing between cells).
Page Formatting
Never use a <table> element to structure documents and position content. The <table> element should only be used for content that is semantically a table (e.g., a data table). If you want to lay out content on your page in columns or a grid, use CSS properties such as a flexbox or grid. Remember that HTML is for semantic annotation, not for appearance!
5.1.9. Forms#
In web pages, a form is a part of a web page that enables a user to interact with the page by providing information. Users can enter input data (in the form of written text, boxes selected, etc), which is then submitted to the web server. Forms are a common part of web pages that you’ve likely used every day - and it’s possible some of your interactions have been through forms without realizing it! Similar to tables, forms are complex structures in HTML; this section describes how to create basic forms and how to make them accessible. For more examples and guidance, see MDN’s guide to web forms.
Web forms are defined by using the <form> element. This block element
annotates a part of the page that will accept data inputted by the user (though <input> elements, described below) and specifies what should
be done with that data when the form is submitted.
When a form is submitted (such as by the user clicking a submit-type <button>), the browser collects the data the user has entered and sends
that data to a server through an HTTP request -similar to the request sent when a user clicks on a hyperlink (this means that submitting a form
loads a new page!) Where and how that data is sent depends on two attributes on the <form> element: the action and the method.
<!-- a very simple form -->
<form action="result.html" method="GET">
<!-- labeling text for the input -->
<label for="greetInput">Provide a greeting:</label>
<!-- Where the user provides input data -->
<input type="text" id="greetInput" name="greeting">
<!-- Submit button -->
<button type="submit">Submit</button>
</form>
The action attribute specifies the URL where the user data will be sent when the form is submitted. This will most likely be a URL on the same domain as the current page, so only a relative path is provided. In the above example, the submitted form data will be sent to the server to the result.html URL; the server will handle that submitted data and then return a response for that URL (in effect, loading that page). Note that servers may do all kinds of things with the submitted data, including redirecting the users so that the page that loads for the user won’t necessarily be the same one as specified in the action.
The method attribute indicates which HTTP Method the resulting HTTP request will use. Valid options are "GET" (for GET requests - used when the request is only retrieving data from the server) or "POST" (for POST requests - used when the request is adding or updating the data stored by the server). Forms that send GET requests will transmit the user-entered data as query parameters (one for each <input>) appended to the action URL - for example, submitting the above form with the user-entered message "Hello" would send an HTTP request to result.html?greeting=Hello. Forms that send POST requests include the user data in the HTTP request’s body (where the server accesses that data). Through web browsers, users can bookmark URLs with query parameters.
React Apps
React apps and similar systems often handle form data on the client side; thus, the action and method attributes are omitted since they aren’t used.
You can prevent a form from sending a new HTTP request using the event.preventDefault() method in JavaScript; see DOM Manipulation for more details.
5.1.9.1. <input> elements#
Users provide data for forms to send through <input> elements. An <input> provides an interactive control, such as a text area or a box to check. <input> elements are inline, empty elements - similar to an <img>, they have no text content of their own.
<!-- example input elements -->
<label for="usernameInput">Username:</label>
<input type="text" id="usernameInput" name="username">
<label for="passwordInput">Password:</label>
<input type="password" id="passwordInput" name="password">
There are many different kinds of <input> elements, specified with the type attribute, including:
text: a single-line text field for users to type into. This is the defaulttypefor an<input>.password: like atextinput, but whatever the user types is obscured (e.g., shown as dots or stars) so that others can’t see it.checkbox: A box that the user can check or uncheck. Note that no text is associated with the input; you need to use a<label>to tell the user what they’re checking!radio: A button the user can select from a set of (the “circle” checkbox). It is called a “radio button” because, like with a radio, you can only select a single channel. Radio buttons are noted as a group by giving them all the samenameattribute.number: A field in which the user can enter a numeric valuedate: An input where the user can select a calendar date. Most browsers will provide a calendar drop-down interface, while also allowing users to type in the date. Atimeinput works similarly, but for selecting the time of day, adatetime-localinput will let users select both at once.file: An input where the user can choose a file from their operating system. By default, this looks like a button that will provide an operating-system-dependent interface for choosing the file when clicked. Note: uploading files requires special handling on the server side, beyond what is required for most form data. Multiple files can be sent at once.hidden: An input that is not shown to the user, though its data values will still be submitted along with the form.hiddeninputs are used for data values that are not directly specified or modified by the user, but may be dynamically generated by the web page or system (e.g., through JavaScript).
Each <input> element may support additional attributes depending on its type. For example, a number input supports min and max attributes
that limit which numbers are allowed. Such attributes can also be used for built-in client-side
form validation, informing users if the values they’ve enetred are invalid.
While <input> is the most common element for collecting user input in a <form>, a couple of others exists:
A
<textarea>provides a larger text box where users can write and format multiple lines of text.A
<select>element provides a drop-down menu; each item in the menu is specified through a child<option>element:
<!-- A simple select dropdown -->
<select name="animal">
<option value="">Choose an Animal</option>
<option value="lion">Lion</option>
<option value="tiger">Tiger</option>
<option value="bear">Bear</option>
</select>
See the documentation for more options when using these elements. Collectively, <input>, <select>, and similar are referred to as form control elements.
All form controls and inputs support and expect a name attribute as in the above examples. This attribute provides an identifier for the input
to distinguish it from others in the form (the form itself doesn’t know what descriptive text is around or labeling the inputs). Note that the name
is distinct from the id attribute - a name is used to identify the user-entered value within the form’s data, while the id is used to identify
the <input> element itself (regardless of what data has been entered). Because id attributes are used with <label> elements for accessibility,
most <input> elements will have both attributes - often, they will have the same identifier making it look redundant, but the attributes serve different purposes.
All inputs also support a value attribute. This attribute stores whatever value the user has inputted or selected. You can use it to provide a “default”
value for an input that the user can replace.
<!-- example inputs with different names and default values -->
<label for="firstnameInput">First Name:</label>
<input type="text" id="firstnameInput" name="firstname" value="Joel">
<label for="passwordInput">Last Name:</label>
<input type="text" id="lastnameInput" name="lastname" value="Ross">
Text-like inputs also support a placeholder attribute, which provides initial text that will be shown to the user but is not a default value (the value is blank "").
Checkboxes, radio buttons, and select dropdowns also use the value attribute to distinguish between options the user has selected:
<!-- a set of radio buttons -->
<label for="smallRadio">Small</label>
<input type="radio" id="smallRadio" name="size" value="small">
<label for="mediumRadio">Medium</label>
<input type="radio" id="mediumRadio" name="size" value="medium">
<label for="largeRadio">Large</label>
<input type="radio" id="largeRadio" name="size" value="large">
When the form is submitted, the data the user has entered is encoded with the name attribute of each input as the identifier or “key”,
and the value attribute as the value. This can be seen in the query parameters of a GET request’s target URL. For example, without
any changes by the user, the above firstname and lastname inputs in the above example would produce a query parameter list that
looks like: ?firstname=Joel;lastname=Ross - the keys (before the =) are the name attributes, and the values (after the =) are
the value attributes. As such, the name attributes are often named and structured to produce data identifiers that are easy for a server to consume.
In addition to <input> and related elements, a <form> will often contain <button>
elements. In particular, a <button type="submit"> will submit its parent form when clicked. It is best practice to have an explicit submit
button (including search forms) and not just rely on the user hitting “enter” - this makes the form more accessible.
5.1.9.2. Labels and Accessibility#
Forms can be quite complex structures in HTML; they are also key to user interaction, so you need to take particular care to ensure that forms are accessible.
One of the most important steps to making a form accessible is to provide labels for all form controls and inputs. Labels are specified by
using the <label> element (which you can see used in previous examples).
The text content inside a <label> element is the text the user sees that describes the input (which are often instructions for what data
the user enters). You explicitly associate a <label> with a particular input using the <label>’s’ for attribute - this attribute takes a
value that is the input element’s id (the id is used to identify an element on the page uniquely):
<!-- this label is a description for the element with the `nameInput` id -->
<label for="nameInput">Name:</label>
<input type="text" id="nameInput" name="name">
By default, when a user clicks on the <label> text, the associated form control element will gain focus (be selected, with the cursor moved
there and ready to receive input). This makes it easier for users to select the relevant input and is particularly helpful for smaller input elements
such as checkboxes that may be harder for a user to target (because of Fitts’ Law).
All form controls should be labeled to support screen readers and other assistive technology. If you don’t want any descriptive text for sighted users
(due to other visual context clues), you can use CSS to hide the <label> visually, but it should always be included.
It is also possible to implicitly associate a <label> and an <input> by making the <input> a child element of the <label>. This can be useful
for dynamically generated content when you may not be able to specify an id for the <input> (e.g., a form where the user can dynamically add more
textboxes). However, explicit association with the for attribute is better supported by assistive technology, so it should be used whenever possible.
<!-- implicitly labeling an input, used only when `id` cannot be specified -->
<label>
Name:
<input type="text" name="name">
</label>
Keeping forms short and simple (with as few inputs as possible) is good for both accessibility and overall usability - generally, users prefer small
forms. For more complex forms, you can also group inputs by putting them together inside of a
<fieldset> element. This is particularly helpful for radio buttons.
The <legend> element is used to give a “title” to a <fieldset>.
<fieldset>
<legend>Programming Language</legend>
<div>
<input type="radio" name="language" id="htmlInput" value="html">
<label for="htmlInput">HTML</label>
</div>
<div>
<input type="radio" name="language" id="cssInput" value="css">
<label for="cssInput">CSS</label>
</div>
</fieldset>
For more guidance on making accessible forms, see WAI’s Forms Tutorial.
5.2. ARIA for Accessibility#
As noted in [Client-Side-Development]{#web-accessibility}, making web pages accessible involves conforming to the W3c’s Web Content Accessibility Guidelines (WCAG), which was developed by the Web Accessibility Initiative (WAI) (a part of the W3c). The WAI has also authored an additional web standard for supporting accessibility. This standard is called the Accessible Rich Internet Applications Suite, or ARIA. ARIA specifies an extension to HTML, defining additional HTML attributes that can be included in elements to provide additional support for screen readers and other assistive technology. These attributes help screen readers navigate through a page (for people who can’t just visually scroll), as well as provide a mechanism by which “rich” interactive web apps (such as those you will create with JavaScript) can communicate their behavior to screen readers.
This section describes some of the most common ARIA-defined attributes that you may use to ensure that web pages are accessible to all users. You will notice that semantic HTML usage makes many ARIA attributes redundant - HTML as a standard has evolved to support accessibility better since the ARIA standard was created. That said, there are still places where HTML falls short, and so ARIA needs to step in.
5.2.1. ARIA Labeling#
A fundamental step in making web pages accessible is to ensure they are perceivable to screen readers. This means that content
cannot just be presented visually. The most common example is including alt attributes for <img> elements, which allows those
visual elements to be perceived and understood by assistive technology.
But the alt attribute is only supported by the <img> element, even though other element types may also be used for purely visual information
(e.g., a <div> that is styled to contain a certain background image, or an <svg> containing graphics). ARIA allows you to provide “alternate text”
for these other elements by using the aria-label attribute, which specifies what text should be read in place of the normal content.
<div class="green-rect" aria-label="a giant green rectangle"></div>
Note that aria-label is only needed for purely visual elements (with no textual content); you don’t need to put an aria-label on every <div> or on any <p>. If the element contains text content - even if that text is nested within multiple child elements - it probably doesn’t need an aria-label.
For longer (multiple sentences) descriptions, you can alternatively include the aria-describedby attribute with reference to the id of a different element on the same page that contains the textual description. aria-describedby takes as a value a fragment reference (similar to a bookmark hyperlink) to the element containing the description.
<div class="green-rect" aria-describedby="#rectDetail"></div>
<p id="rectDetail">The above rectangle is giant and green.</p>
The descriptive text can be anywhere else on the page; having a visual element separated from its description by multiple other elements is fine.
As with bookmark links, notice that the aria-describedby value starts with the # fragment symbol, but the target element’s id does not.
Additionally, note that while aria-label attributes replaces its elements content, the aria-describedby attribute will be read after
the element’s content, as it provides more information (also compare this to the similar
aria-labelledby attribute).
Note
Overall, the best way to make purely visual information accessible is to not have purely visual information in the first place! A page’s content should always support non-visual users and browsers: include textual descriptions and captions for images, use text labels instead of icons for buttons, etc. This is the best way to support universal design.
While alt attributes and aria-label will let you make visual elements perceivable, some images (or other elements) are purely decorative: company logos, icons accompanying text descriptions on buttons, etc. You can make this content more accessible and cause a screen reader to “skip” reading these decorative elements by using another ARIA attribute aria-hidden="true". An element with this attribute will still appear on the rendered page; it will just be ignored by screen readers. (This attribute is an exception to the general rule that boolean attributes don’t take values).
<!-- a search button with an icon -->
<!-- the icon img will not be read, but the button text will be -->
<button>
<img src="search_icon.png" aria-hidden="true">
Search
</button>
Note that elements hidden through CSS (e.g., using display:none) are already removed from what a screen reader reads, so they do not need the additional ARIA attribute.
5.2.2. ARIA Roles and Landmarks#
The ARIA specification defines a role attribute that details the semantic purpose of an element for screen readers in case that purpose is only clear through visual styling.
For example, A page may have a <div> or an <svg> that has been styled to look and act like button; the ARIA attribute helps screen readers understand that this element can be pressed to cause an action:
<div class="btn" role="button">Submit</div>
From the screen reader’s perspective, this element acts like a <button>, even if it’s actually a different element type for some reason.
There are a large number of different ARIA roles of different roles,
including roles for structure and interactive widgets such as a popup dialog, a switch, or a menu. Some of these, such as button in the
above example, are redundant with semantic elements added after the ARIA standard was created. It best to always use elements that reflect
their semantic purpose instead of using ARIA roles. If an element will be clicked to cause an action, you should use a <button> instead of
role="button" - then style it to look how you want with CSS.
In addition, the ARIA role attribute can indicate landmark regions.
A landmark role indicates specific locations in the document and allows a screen reader to easily “jump” to that section of content.
This helps ensure that the page is navigable. Landmark roles include:
banner: content that is related to the website as a whole rather than to the specific page itself (named after the “banner” often found at the top of a page). The<header>sectioning element implies this role.main: the “main” content. This is useful for allowing the screen reader to jump to the meat of the page, past e.g., the banner and navigation sections. The<main>sectioning element implies this role.contentinfo: a section that contains information about the webpage, such as author contact info and copyright information. This is usually found at the bottom of the page. The<dooter>sectioning element implies this role.navigation: a section of links for moving around the page, such as a navigation bar. This allows screen readers to jump to the “quick links” and move around the page as rapidly as possible. Note, though, that listening to all of the links read can be time-consuming, so allowing the user to jump to the rest of the content is also important (using a technique called a “skip link”). The<nav>sectioning element implies this role.
As noted, these landmark roles are also implied by sectioning elements; you do not need to include a role on these elements, and it is best practice to use the explicit elements themselves rather than the landmark roles!
Tip
Considering your content in terms of these roles can be a good design trick for figuring out how to organize your page in a way that will be understandable and accessible to all users!
5.2.3. ARIA and Interactivity#
ARIA also provides accessibility support for interactive web pages, where the displayed content may change as the page is being used, by providing attributes that explicitly note the state of the web application.
For example, consider a page with a hamburger menu) or similar dropdown or “slide-out” content.
Assistive technologies need to inform the users about whether or not the hamburger menu is expanded or whether the sliding content is visible.
This can be managed through ARIA state attributes such as aria-expanded:
<!-- an example hamburger menu with ARIA attributes -->
<button aria-expanded="false" aria-controls="menuContent" aria-label="Expand Menu">
<img src="hamburger-icon" alt="a hamburger menu">
</button>
<ul id="menuContent" class="collapsed">
<li>Link 1</li>
<li>Link 2</li>
...
</ul>
In the above example, the <button> element controls whether or not the <ul> is expanded or not - in the current HTML, the <ul> is collapsed (hidden) through a CSS class. By using JavaScript (see DOM Manipulation), clicking the button can cause the CSS styling of the <ul> to change so that it is no longer collapsed and is instead visible.
The ARIA attribute aria-expanded is placed on the the button to indicate to assistive technologies that this button is (a) an interactive control
for part of the page and (b) that it’s current state is that it is not collapsed. When the button is pressed, the JavaScript would also change
the aria-expanded attribute to have a value of "true", now indicating to screen readers that the menu is open. The aria-controls references
the id of the element that the button is adjusting (telling screen readers what element has been modified and controlled).
Different interactive widgets and patterns may require other ARIA attributes to best support assistive technologies. When looking at examples of such widgets (e.g., those provided by Bootstrap), be sure to check for appropriate ARIA attributes and consult ARIA documentation for best practices.
Important
The WAI strongly states that No ARIA is better than Bad ARIA. Misusing ARIA attributes can grossly misinform users of assistive technologies, making it harder for them to understand the page - instead of unclear semantics, they may be wrong. Good semantic HTML usage will go far in making web pages accessible; ARIA can and should be used only to fill in explicit gaps where the semantic HTML falls short.
5.3. Resources#
Most details on specific HTML elements can be found in the resources noted in previous chapters (e.g., MDN). Some additional resources for writing accessible HTML are listed below.
Teach Access Tutorial for creating accessible web pages. Provides lost of examples and details. Be sure and check out the code checklist and the design checklist.
UW Accessibility Checklist a thorough and detailed list of considerations for developing accessible systems put together by UW. See also their complete list of tools and resources.
Using ARIA (w3c) official guidance for using the ARIA specification
5.4. Original Info 340 Acknowledgements#
Chapter was originally authored by Joel Ross and Mike Freeman. Some content was originally adapted from tutorials by David Stearns. Some structure and explanations inspired by Learning Web Design by Jennifer Robbins.