Back to journal
Frontend

I Replaced 300 Lines of JavaScript with 10 Lines of Modern HTML

Dialog modals, accordions, color pickers, lazy loading — HTML can do all of this natively now. Here's what you can delete from your codebase today.

PMML Engineering · Studio 26 May 2026 6 min read 0 views
I Replaced 300 Lines of JavaScript with 10 Lines of Modern HTML

Stop shipping JavaScript for things the browser handles natively. Every line of JS you remove is faster load times, fewer bugs, and better accessibility — for free.

1. The <dialog> Element — Delete Your Modal Library

Clean code on screen

Before: 200+ lines of React modal code with portal, focus trap, scroll lock, backdrop click, and escape key handling.

Now:

<dialog id="confirm-dialog">
  <h2>Are you sure?</h2>
  <p>This action cannot be undone.</p>
  <form method="dialog">
    <button value="cancel">Cancel</button>
    <button value="confirm">Confirm</button>
  </form>
</dialog>

<button onclick="document.getElementById('confirm-dialog').showModal()">
  Delete Account
</button>
dialog::backdrop {
  background: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(4px);
}

dialog[open] {
  animation: slide-up 0.3s ease;
}

You get focus trapping, scroll locking, Escape key closing, and backdrop click closing — all for free. Zero JavaScript beyond the one-line .showModal() call.

2. The <details> Element — Delete Your Accordion Component

<details>
  <summary>What's your refund policy?</summary>
  <p>We offer full refunds within 30 days of purchase, no questions asked.</p>
</details>

<details>
  <summary>Do you offer team pricing?</summary>
  <p>Yes! Teams of 5+ get 20% off. Contact sales for enterprise pricing.</p>
</details>
details summary {
  cursor: pointer;
  padding: 1rem;
  font-weight: 600;
  border-bottom: 1px solid #e2e8f0;
}

details[open] summary {
  color: #2563eb;
}

details > :not(summary) {
  padding: 1rem;
  animation: expand 0.2s ease;
}

Accessible, keyboard-navigable, screen-reader-friendly. No useState, no onClick, no ARIA attributes needed.

3. Native Lazy Loading — Delete Your Intersection Observer

Before:

// 25 lines of IntersectionObserver boilerplate
const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      observer.unobserve(img);
    }
  });
}, { rootMargin: "200px" });

document.querySelectorAll("img[data-src]").forEach((img) => {
  observer.observe(img);
});

After:

<img src="photo.jpg" loading="lazy" alt="Product photo" />
<iframe src="/embed" loading="lazy"></iframe>

Performance optimization

One attribute. Works on images and iframes. Browser handles the intersection logic, resource prioritization, and network scheduling better than any JavaScript library.

4. The <input> Power Features You're Ignoring

<!-- Color picker — no library needed -->
<input type="color" value="#2563eb" />

<!-- Date picker — native, accessible -->
<input type="date" min="2026-01-01" max="2026-12-31" />

<!-- Search with clear button + datalist autocomplete -->
<input type="search" list="suggestions" />
<datalist id="suggestions">
  <option value="React" />
  <option value="Next.js" />
  <option value="TypeScript" />
  <option value="Tailwind CSS" />
</datalist>

<!-- Pattern validation — no regex in JS -->
<input type="text" pattern="[A-Z]{2}[0-9]{4}" 
       title="2 uppercase letters followed by 4 digits" />

5. popover API — Delete Your Tooltip/Dropdown Library

<button popovertarget="menu">Options ▾</button>

<div popover id="menu">
  <button>Edit</button>
  <button>Duplicate</button>
  <button>Delete</button>
</div>
[popover] {
  padding: 0.5rem;
  border-radius: 0.5rem;
  box-shadow: 0 10px 25px rgb(0 0 0 / 0.15);
}

Modern web development

Auto-closes when clicking outside. Auto-positions. Manages z-index. Handles Escape key. All native. All free.

What You Can Delete Today

| Feature | Old Way | Native HTML | |---------|---------|-------------| | Modal dialog | 200+ lines + library | <dialog> + 1 line JS | | Accordion/FAQ | 50-100 lines React | <details> | | Lazy loading | IntersectionObserver | loading="lazy" | | Color picker | react-color (30KB) | <input type="color"> | | Dropdown menu | Headless UI / Radix | popover | | Autocomplete | Custom component | <datalist> | | Form validation | Yup/Zod (client) | pattern + required |

The Rule

Before installing a package or writing a component, Google: "HTML <element> native". You might be surprised what the platform gives you for free in 2026.

Tag a dev who needs to see this. Share on Twitter and watch the replies.

#html#webdev#javascript#performance

Keep reading

You might also like