As you know, we love unusual tech at Lingo.dev. So when in my last essay, David commented he was surprised to not see "dialog" HTML element, it hit me - wait, but there's so many HTML5 elements that we don't even know about!
In our rush to embrace sophisticated frameworks and libraries, we've overlooked some of the most elegant solutions built right into HTML5. There's something beautiful about achieving with a single tag what might otherwise take dozens of lines of JavaScript.
The best code is often the code you don't have to write. How many times have you reached for a JavaScript library to create a modal window? Or written complex CSS for collapsible sections? What if I told you that HTML5 already has native elements for these exact purposes?
These aren't just any HTML elements - they're the ones that make you go "Wait, we could do that all along?" Let's explore eight surprisingly powerful HTML elements you probably aren't using - but absolutely should be.
1. The <dialog>
Element: Native Modal Windows
Picture this: you're starting a new project and need to add a modal dialog. Your first instinct? Reach for React, grab a UI library, or write a bunch of JavaScript. I get it - I've done the same thing countless times.
But HTML5 has a built-in element specifically designed for this purpose:
<dialog id="myDialog">
<h2>This is a Native Dialog</h2>
<p>No JavaScript frameworks required!</p>
<button id="closeDialog">Close</button>
</dialog>
<button id="openDialog">Open Dialog</button>
You still need a bit of JavaScript to handle the open/close actions:
const dialog = document.getElementById('myDialog');
document.getElementById('openDialog').addEventListener('click', () => {
dialog.showModal(); // Makes the dialog appear as a modal
});
document.getElementById('closeDialog').addEventListener('click', () => {
dialog.close(); // Closes the dialog
});
The browser handles focus management, backdrop rendering, and keyboard accessibility. Most developers I talk to have never used this element, despite it being supported in all major browsers since around 2022.
You can style it however you want:
dialog {
padding: 20px;
border-radius: 8px;
border: 1px solid #ddd;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
dialog::backdrop {
background-color: rgba(0,0,0,0.6);
}
I've been using this in production for a couple of years now. The main gotcha is iOS Safari support, which was spotty until 2023, but it's solid now. For complex modals with lots of interactive elements, you might still want a dedicated library, but for most use cases, the native element works perfectly.
2. The <details>
and <summary>
Elements: Native Accordions
Accordions are everywhere on the web - FAQs, product details, settings panels. Most developers reach for a JavaScript solution, but HTML5 has native elements for this:
<details>
<summary>Click to expand</summary>
<p>This content can be expanded and collapsed without any JavaScript!</p>
</details>
That's all you need. The browser handles the toggle functionality, accessibility attributes, and keyboard navigation.
You can style it to match your design system:
details > summary {
list-style: none; /* Removes the default triangle */
}
details > summary::before {
content: '▶';
display: inline-block;
margin-right: 0.5em;
transition: transform 0.2s;
}
details[open] > summary::before {
transform: rotate(90deg);
}
These elements are particularly useful for nested navigation. I used them recently in a documentation site where we needed three levels of collapsible navigation:
<details>
<summary>Programming Languages</summary>
<details>
<summary>Frontend</summary>
<ul>
<li>HTML</li>
<li>CSS</li>
<li>JavaScript</li>
</ul>
</details>
<details>
<summary>Backend</summary>
<ul>
<li>Python</li>
<li>Java</li>
<li>Ruby</li>
</ul>
</details>
</details>
The main limitation is animation - if you need smooth animations when opening/closing, you'll need to add JavaScript. But for many use cases, the default behavior is perfectly fine.
3. The <datalist>
Element: Native Autocomplete
Autocomplete functionality is a staple of modern web forms. Most developers reach for a third-party solution, but HTML5 has a built-in element for this:
<input list="browsers" name="browser" placeholder="Choose a browser">
<datalist id="browsers">
<option value="Chrome">
<option value="Firefox">
<option value="Safari">
<option value="Edge">
</datalist>
This gives you a text input that shows suggestions as you type, with the ability to select from the list or enter custom text.
One interesting use case is creating a color picker with named colors:
<input type="text" list="colors" placeholder="Choose a color">
<datalist id="colors">
<option value="#FF0000">Red</option>
<option value="#00FF00">Green</option>
<option value="#0000FF">Blue</option>
</datalist>
The user can either type a color name or a hex code. Add a bit of JavaScript to show a preview:
const input = document.querySelector('input');
const preview = document.getElementById('colorPreview');
input.addEventListener('input', () => {
preview.style.backgroundColor = input.value;
});
The main limitation of <datalist>
is styling - you can't easily customize the appearance of the dropdown options. If you need custom styling for the suggestions, you might still need a JavaScript solution. But for many use cases, the native element is perfectly adequate.
4. The <meter>
Element: Semantic Measurement Display
When displaying values within a known range, most developers use a div with a background color. But HTML5 has a dedicated element for this:
<meter value="75" min="0" max="100" low="30" high="70" optimum="80"></meter>
The browser styles it based on the thresholds you set. If the value is in the "low" range, it might show as yellow. If it's in the "high" range, it might show as orange. And if it's near the "optimum" value, it shows as green.
This is particularly useful for dashboards:
<!-- Disk usage (lower is better) -->
<meter value="75" min="0" max="100" low="70" high="90" optimum="0">75%</meter>
<!-- Battery level (higher is better) -->
<meter value="35" min="0" max="100" low="20" high="60" optimum="100">35%</meter>
<!-- CPU usage (lower is better) -->
<meter value="82" min="0" max="100" low="60" high="80" optimum="0">82%</meter>
The optimum
attribute indicates whether higher or lower values are better, which affects the color.
Browser support is good, but styling options are limited. If you need a highly customized appearance, you might still need a custom solution. But for dashboards and monitoring UIs, the native element works well and communicates semantic meaning to screen readers.
5. The <output>
Element: Dynamic Calculation Results
When building calculators or interactive forms, most developers use a div to show the result. But HTML5 has a dedicated element for this:
<form oninput="result.value = parseInt(a.value) + parseInt(b.value)">
<input type="number" id="a" value="0"> +
<input type="number" id="b" value="0"> =
<output name="result" for="a b">0</output>
</form>
The for
attribute creates an explicit relationship between the inputs and the output, which helps screen readers understand that the output is a result of those specific inputs.
I used this in a side-project last year for a mortgage calculator:
<form
oninput="
const P = principal.valueAsNumber,
r = (rate.valueAsNumber/100)/12,
n = term.valueAsNumber*12,
m = r ? (P*r)/(1-Math.pow(1+r,-n)) : P/n;
payment.value = m.toFixed(2);
totalInterest.value = (m*n - P).toFixed(2);
"
>
<input id="principal" type="number" value="200000" />
<input id="rate" type="number" value="3.5" step="0.1" />
<input id="term" type="number" value="30" />
Payment $<output id="payment" for="principal rate term">0.00</output> Interest
$<output id="totalInterest" for="principal rate term">0.00</output>
</form>
The main advantage is semantic meaning - screen readers can announce that the value is a calculation result. It's also a form element, so it can be included in form submissions if needed.
6. The <mark>
Element: Semantic Highlighting
When highlighting text, most developers use a span with a background color. But HTML5 has a dedicated element for this:
<p>The <mark>quick brown fox</mark> jumps over the lazy dog.</p>
By default, browsers style it with a yellow background, but you can customize it:
mark {
background-color: #fff9c4;
padding: 2px 4px;
border-radius: 3px;
}
This is particularly useful for search results:
const searchTerm = "HTML";
const content = "HTML elements are the building blocks of HTML pages.";
const highlighted = content.replace(
new RegExp(searchTerm, 'gi'),
match => `<mark>${match}</mark>`
);
The advantage is semantic meaning - screen readers can announce that text is highlighted, giving users a better understanding of why certain text stands out.
I've found this especially useful in documentation sites and knowledge bases, where highlighting search terms helps users quickly find what they're looking for.
7. The <time>
Element: Semantic Dates and Times
Most developers display dates and times using regular text elements, but HTML5 has a dedicated element for this:
<p>The article was published on <time datetime="2025-05-20">May 20, 2025</time>.</p>
The datetime
attribute allows you to specify the date in a standardized format (ISO 8601) that machines can understand, while displaying a human-friendly format to users.
This is useful for search engines, which can extract the exact date, and for browsers or extensions that might want to offer features like adding events to calendars.
You can use it for various date and time formats:
<!-- Just a date -->
<time datetime="2025-05-20">May 20, 2025</time>
<!-- Date and time -->
<time datetime="2025-05-20T14:30:00">2:30 PM on May 20, 2025</time>
<!-- Just a time -->
<time datetime="14:30:00">2:30 PM</time>
<!-- A duration -->
<time datetime="PT2H30M">2 hours and 30 minutes</time>
One practical application is for relative times:
<p>Posted <time datetime="2025-05-18T14:30:00" class="relative-time">2 days ago</time></p>
With JavaScript, you can update these periodically:
function updateRelativeTimes() {
document.querySelectorAll('.relative-time').forEach(el => {
const date = new Date(el.getAttribute('datetime'));
el.textContent = getRelativeTimeString(date);
});
}
// Update every minute
setInterval(updateRelativeTimes, 60000);
This element is particularly valuable for blogs, news sites, and social media platforms, where accurate date and time information is important.
8. The <figure>
and <figcaption>
Elements: Semantic Image Captions
Most developers implement image captions with divs and paragraphs, but HTML5 has dedicated elements for this:
<figure>
<img src="chart.jpg" alt="Sales chart for Q2 2025">
<figcaption>Fig.1 - Company sales increased by 25% in Q2 2025.</figcaption>
</figure>
The <figure>
element isn't just for images - it can be used for any content that is referenced as a single unit:
<!-- Code snippet with caption -->
<figure>
<pre><code>
function greet(name) {
return `Hello, ${name}!`;
}
</code></pre>
<figcaption>A simple JavaScript greeting function using template literals.</figcaption>
</figure>
<!-- Quote with attribution -->
<figure>
<blockquote>
<p>The best way to predict the future is to invent it.</p>
</blockquote>
<figcaption>— Alan Kay</figcaption>
</figure>
These elements are particularly useful for content management systems and blogs, where editors need to add captions to images and other media.
A simple CSS setup can create a nice-looking gallery:
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
}
figure {
margin: 0;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
transition: transform 0.3s;
}
figure:hover {
transform: translateY(-5px);
}
figure img {
width: 100%;
height: 200px;
object-fit: cover;
display: block;
}
figcaption {
padding: 10px;
background-color: #f5f5f5;
font-style: italic;
}
The main advantage is semantic meaning - screen readers can announce that the text is a caption for the associated content, improving accessibility.
Conclusion?
Embrace the Power of Native HTML.
We've explored eight HTML5 elements that offer elegant, built-in solutions to common web development challenges:
-
<dialog>
for native modal windows -
<details>
and<summary>
for collapsible content -
<datalist>
for native autocomplete -
<meter>
for semantic measurement display -
<output>
for dynamic calculation results -
<mark>
for semantic highlighting -
<time>
for semantic dates and times -
<figure>
and<figcaption>
for semantic image captions
These elements reduce dependency on JavaScript, improve accessibility, and make your HTML more semantic and meaningful.
They're not perfect for every use case - building Lingo.dev oftentimes requires more customization or browser support than they can provide. But they're worth considering before reaching for a third-party solution.
What other HTML elements have you found useful in your projects? Let me know in the comments!
Useful links:
- Lingo.dev on Twitter/X - dev + fun;
- GitHub repo - give it a star :)
- Discord - users told we need a Discord, so we created one 👀
Top comments (48)
Great tips, im using a bunch of them on my project's website.
I'm gonna add an other one to your list if you let me ;) : you can also achieve scroll interactions using link anchors and ids same as before using "#top" for example but now smoothly.
Just add the CSS rule:
wow, this is really nice! :)
Nice article!
Maybe you can add some screenshots of what every element looks like by default?
ah nice, good idea!
Cool!
time
right? this is pretty cool!
WoW! It's been a while I looked into HTML tags. Never knew about
<details>
,<summary>
,<output>
and<meter>
. I'm already thinking where can I use this in my project.<meter>
is my favorite, basically a progress bar!I wish browsers would render it using more colors than red, yellow, and green.
yep, 100%
These sound appealing, but offer limited ability for custom styling. You are forced to use default browser styles, which look different across different browser engines. If you want full control over appearance and UX it’s best just use a UI library or build your own.
yep, i agree with this! unless you already use tailwind that can be added on top.
<meter>
is going to be a fun one to mess around with! This is awesome post!yeah indeed! :)
pretty cool seeing all these native elements getting some real use - you think people avoid them just because it’s habit at this point or something bigger?
I think we, on average, got used to searching for 3rd party libs right away, or using chatgpt trained on mostly mature websites, instead of studying the fundamentals!
Excellent article 😁👌
Love this roundup! The dialog and meter elements especially always surprise me - do you have a favorite use case where going native saved you from a giant dependency?
Meter is nice, in my opinon, when you have to show a quick unsophisticated meter in the app :)
Awesome
Some comments may only be visible to logged-in visitors. Sign in to view all comments.