Semantic HTML in Blazor Server
Say what it is,
not how it looks
How meaningful markup makes Blazor applications more accessible, maintainable, resilient, and understandable.
Blazor lets us build interactive web applications with C# and
Razor components. That changes how we write application logic, but it does not
change what the browser receives: HTML.
That last
detail matters.
A polished Blazor application can look correct while still
being difficult to navigate with a keyboard, confusing to assistive technology,
hard for search engines to interpret, and unnecessarily complicated to
maintain. Semantic HTML helps solve these problems by giving content a clear
purpose.
Instead of describing how an element should look, semantic
HTML describes what the element is.
<!-- Describes presentation
through class names -->
<div class="top-navigation">...</div>
<div class="page-content">...</div>
<!-- Describes purpose through native elements -->
<nav aria-label="Primary navigation">...</nav>
<main>...</main>
Both versions can be styled identically. The second version
also communicates meaning to the browser, accessibility APIs, search tools, and
anyone maintaining the component later.
This article walks through the decisions demonstrated in the
accompanying Blazor Server sample application.
What semantic HTML means
Semantic HTML uses elements according to the purpose of
their content.
For example:
• <nav>
identifies a group of navigation links.
• <main>
contains the page's unique primary content.
• <article>
represents a composition that can stand on its own.
• <section>
groups content around a specific topic, normally with a heading.
• <aside>
contains related information that is not essential to the main flow.
• <button>
performs an action.
• <a
href> navigates to another location.
A <div> is not inherently wrong.
It is useful when an element exists only for layout, styling, or grouping and
no more specific element fits. Problems begin when every part of the interface
becomes a <div>, regardless of purpose.
That pattern is sometimes called "div soup." It
makes the document's meaning dependent on class names and application code.
Why semantic HTML is important in Blazor
1. It improves accessibility by default
Browsers use semantic elements to build an accessibility
tree. Screen readers and other assistive technologies use that tree to
communicate an interface's structure, roles, names, and states.
When a Blazor layout contains real landmarks, a
screen-reader user can move directly between navigation, main content, and
supporting regions. When a control is a real <button>,
it receives the expected keyboard behavior and is announced as a button without
adding a custom role.
Consider an action implemented with a generic element:
<div class="button"
@onclick="Save">Save</div>
This may respond to a mouse click, but it is not
automatically focusable; it does not support the expected keyboard activation
behavior, and it is not exposed as a button.
The native version is both shorter and more capable:
<button type="button"
@onclick="Save">Save</button>
Semantic HTML does not eliminate the need for accessibility
testing, but it gives every component a much stronger starting point.
2. It reduces custom code
Native HTML includes behavior that developers frequently
rebuild with C#, JavaScript, and ARIA attributes.
The sample application uses <details>
and <summary>
for disclosure content:
<details>
<summary>Why not build a custom
accordion?</summary>
<p>
The details element already
provides an expanded state,
keyboard interaction, and a
focusable trigger.
</p>
</details>
No Blazor event handler is required. The control continues
to work while a server circuit is reconnecting because the behavior belongs to
the browser.
The same principle applies to labels, fieldsets, tables,
progress indicators, meters, links, and buttons. Native elements are usually
the smallest reliable implementation.
3. It makes components easier to understand
Razor components are read far more often than they are
written. Meaningful elements make a component's structure visible without
requiring the reader to trace CSS classes.
Compare these two blog-card components:
<div class="post">
<div
class="title">Semantic HTML in Blazor</div>
<div
class="meta">August 22, 2026</div>
<div
class="content">...</div>
<div
class="related-note">...</div>
</div>
<article>
<header>
<h2>Semantic HTML in
Blazor</h2>
<time
datetime="2026-08-22">August 22, 2026</time>
</header>
<p>...</p>
<aside>...</aside>
</article>
The second version communicates the relationship between the
title, publication date, article body, and supporting note. It is easier to
review and less dependent on naming conventions.
4. It creates a better document for search and automation
Search engines and other automated tools use document
structure as one signal when interpreting a page. Clear headings, article
boundaries, navigation regions, figures, captions, and machine-readable dates
make content easier to understand.
Semantic HTML is not a shortcut to search rankings. It does,
however, provide a clearer and more consistent representation of the content
being indexed.
5. It makes the application more resilient
Blazor Server applications normally depend on an active
server connection for interactive C# event handlers. Native HTML behavior does
not have that dependency.
A link still navigates. A disclosure widget still opens. A
labeled input still has an accessible name. A table still communicates its row
and column relationships.
This is progressive enhancement in practice: start with a
useful HTML document, then add Blazor behavior where application-specific
interactivity is genuinely needed.
Start with a semantic application shell
The sample places site-wide landmarks in MainLayout.razor.
This is the natural location because the layout already owns the content
repeated across routes.
@inherits LayoutComponentBase
<a class="skip-link" href="#main-content">
Skip to main content
</a>
<header class="site-header">
<a class="brand"
href="/" aria-label="Elemental home">
Elemental
</a>
<nav aria-label="Primary
navigation">
<ul>
<li><a
href="/#principles">Principles</a></li>
<li><a
href="/#before-after">Examples</a></li>
<li><a
href="/#checklist">Checklist</a></li>
</ul>
</nav>
</header>
<main id="main-content" tabindex="-1">
@Body
</main>
<footer class="site-footer">
Built with Razor components and
native HTML.
</footer>
This layout establishes several useful conventions:
• The
skip link lets keyboard users bypass repeated navigation.
• The
navigation landmark has an accessible name.
• The
page has one visible <main> landmark.
• Repeated
site content remains outside the main region.
• @Body
supplies the unique content for the active route.
The tabindex="-1" on <main>
allows it to receive programmatic focus without adding it to the normal tab
order. This is useful for skip-navigation behavior and route-change focus
management.
Build each page as a real document
Inside the layout, the sample's home page is an <article>
because it is a complete field guide that could stand on its own. Its major
topics are represented by labeled sections.
<article
aria-labelledby="guide-title">
<header>
<p>A field guide for Blazor
developers</p>
<h1
id="guide-title">Semantic HTML in Blazor Server</h1>
<p>...</p>
</header>
<section
aria-labelledby="principles-title">
<h2
id="principles-title">Start with meaning</h2>
<p>...</p>
</section>
<section
aria-labelledby="elements-title">
<h2
id="elements-title">Choose by purpose</h2>
<p>...</p>
</section>
<footer>
Published <time
datetime="2026-08-22">August 22, 2026</time>
</footer>
</article>
The heading hierarchy is deliberate:
1. The
page has one <h1> describing its overall
purpose.
2. Each
major topic uses an <h2>.
3. Subtopics
inside those sections use <h3> elements.
Heading levels should reflect nesting, not preferred font
size. CSS controls presentation; HTML communicates structure.
Choose links and buttons by behavior
Links and buttons often look similar after styling, but they
have different responsibilities.
Use a link when the result is navigation:
<a
href="/articles/semantic-html">Read the article</a>
Use a button when the result is an action on the current
page:
<button type="button"
@onclick="OpenPreview">Open preview</button>
Do not choose between them based on appearance. A link can
be styled like a prominent call to action, and a button can be visually subtle.
The element should describe what activating it does.
Give structured data real relationships
Semantic HTML is particularly valuable for forms and tables
because relationships are difficult to communicate through visual layout alone.
Group related form controls
Use a <fieldset> and <legend>
when several controls answer one question:
<fieldset>
<legend>Which layer should own
meaning?</legend>
<label>
<input type="radio"
name="meaning" value="html" />
HTML structure
</label>
<label>
<input type="radio"
name="meaning" value="css" />
CSS classes
</label>
</fieldset>
The legend gives the group a shared name, while each label
names an individual option.
Describe tables with headers and captions
<table>
<caption>Common interface needs
and their native elements</caption>
<thead>
<tr>
<th
scope="col">When you need</th>
<th
scope="col">Start with</th>
</tr>
</thead>
<tbody>
<tr>
<th
scope="row">Navigation</th>
<td><code><a href></code></td>
</tr>
</tbody>
</table>
The visual position of a cell may be obvious to a sighted
reader. The caption, header cells, and scope attributes preserve
those relationships when the table is read through assistive technology.
Use ARIA to fill gaps-not replace HTML
ARIA can describe roles, states, and relationships that
native HTML cannot express. It is valuable when building advanced composite
widgets or naming multiple landmarks of the same type.
It should not be the first tool used to recreate an element
that already exists.
<!-- Avoid recreating a button
-->
<div role="button" tabindex="0">Save</div>
<!-- Prefer the native element -->
<button type="button">Save</button>
Adding role="button" does not
automatically add keyboard activation, form behavior, disabled behavior, or all
browser conventions associated with a real button. Native HTML provides the
complete package.
A practical rule is: use the correct native element first,
then add ARIA only when the element needs information that HTML cannot provide
by itself.
A five-minute semantic review for Blazor pages
Before shipping a Razor component, perform this short
review.
Read only the headings
Do the headings describe the page when read by themselves?
Do their levels reflect the structure without skipping from <h1>
to <h3>?
List the landmarks
Is there one main region? Does each navigation landmark have
a useful name? Are supporting notes truly appropriate as asides?
Tab through the interface
Can every interactive element be reached? Is focus visible?
Does each control have a clear name? Do links navigate and buttons perform
actions?
Check the page without relying on CSS
Does the source order still make sense? Are relationships
communicated through elements and labels rather than position, color, or class
names alone?
Inspect the browser's accessibility tree
Confirm the roles, names, states, heading levels, and
landmark structure produced by the rendered application. Testing the Razor
source is not enough; the browser's final interpretation is what users receive.
The takeaway
Semantic HTML is not an optional layer added after a Blazor
application is complete. It is part of the component's design.
Choosing meaningful elements gives browsers more
information, gives users more reliable behavior, and gives developers clearer
code. It can reduce custom event handling, reduce unnecessary ARIA, improve
resilience during server reconnects, and make every Razor component easier to
understand.
Blazor gives us a powerful way to build web applications
with C#. Semantic HTML ensures the result is still a strong web document.
The best question to ask before adding another element is
simple:
What is this content, and which
HTML element already describes it?
Start with that answer. Add CSS for appearance and Blazor
for application behavior. Let HTML carry the meaning.

Comments
Post a Comment