You don't need JavaScript

11 old vs modern native HTML & CSS techniques, no library required, side by side.

Browser compatibility:

HTML inputmode and enterkeyhint for Mobile Keyboards

Widely available
Old way
/* Hack: use type=tel to get numeric keyboard */<input type="tel" pattern="[0-9]*" />/* No way to control the return key label */if (/Mobi/.test(navigator.userAgent))   {  submitBtn.textContent = 'Search';}
Modern
<!-- Numeric pad, no type=tel hack --><input type="text" inputmode="numeric" /><!-- Search keyboard with Search return key --><input type="search" inputmode="search" enterkeyhint="search" />

HTML output Element for Live Form Output

Widely available
Old way
<input type="range" id="volume" /><div id="display"></div> const input = document.getElementById('volume');const display = document.getElementById('display');input.addEventListener('input', () =>  {  display.textContent = input.value;});
Modern
<input type="range" id="volume" name="volume" /><output for="volume">50</output>

Lazy Load Images in HTML with loading="lazy"

Widely available
Old way
<!-- HTML: data-src instead of src --><img data-src="photo.jpg" class="lazy" /> const observer = new IntersectionObserver(([entry]) =>  {  if (entry.isIntersecting)  {    entry.target.src = entry.target.dataset.src;     observer.unobserve(entry.target);  }});document.querySelectorAll('.lazy').forEach(img => observer.observe(img));
Modern
<!-- Above the fold: no lazy --><img src="hero.jpg" alt="Hero" /><!-- Below the fold: lazy load --><img src="product.jpg" alt="Product" loading="lazy" />

CSS Accordion with details and summary (No JavaScript)

Widely available
Old way
<!-- HTML: custom markup --><button aria-expanded="false" aria-controls="panel">  What is CSS?</button><div id="panel" hidden>  <p>CSS is a stylesheet language.</p></div>
Modern
<!-- No JS, no aria wiring --><details>  <summary>What is CSS?</summary>  <p>CSS is a stylesheet language.</p></details>

HTML datalist: Native Autocomplete (No JavaScript)

Limited availability
Old way
/* Add autocomplete library (Awesomplete, Typeahead, etc.) */import Awesomplete from 'awesomplete';new Awesomplete(document.querySelector('#country'),  {  list: ['Afghanistan', 'Albania', 'Algeria', ...]});/* Plus custom CSS for the dropdown */
Modern
<input list="countries" id="country" name="country" /><datalist id="countries">  <option value="United States" />  <option value="Canada" />  <option value="Mexico" /></datalist>

CSS Exclusive Accordion with the details name Attribute

Widely available
Old way
// Close all on toggle, reopen the clicked onedocument.querySelectorAll('details')  .forEach(d => {    d.addEventListener('toggle', () => {      if (d.open)        others.forEach(o => o.open = false);    });  });
Modern
<details name="faq">  <summary>Question 1</summary>  <p>Answer 1</p></details><details name="faq">  <summary>Question 2</summary>  <p>Answer 2</p></details>

CSS Modal Controls with commandfor and command Attributes

Limited availability
Old way
<!-- Inline onclick or JS event listener --><button onclick="  document.querySelector('#dlg') // [!code --]  .showModal()"> // [!code --]  Open Dialog</button><dialog id="dlg">...</dialog>
Modern
<button commandfor="dlg" command="show-modal">  Open Dialog</button><dialog id="dlg">...</dialog>

CSS Dialog Light Dismiss with closedby Attribute

Limited availability
Old way
/* JS: detect click outside dialog bounds */dialog.addEventListener('click', (e) =>  {  const rect = dialog.getBoundingClientRect();   if (e.clientX < rect.left ||      e.clientX > rect.right ||      e.clientY < rect.top ||      e.clientY > rect.bottom)  {    dialog.close();  }});
Modern
<dialog closedby="any">  Click outside or press ESC to close</dialog>

Customizable CSS Select with appearance: base-select

Limited availability
Old way
/* JS: replace native select with custom DOM */import Choices from 'choices.js';new Choices('#my-select',   {  searchEnabled: false,});/* Plus ~30 lines of custom CSS for   .choices__inner, .choices__list, etc. */
Modern
select,select ::picker(select)   {  appearance: base-select;}select option:checked   {  background: var(--accent);}

CSS-Only Dropdown Menu with the Popover API

Newly available
Old way
.menu   {  display: none;}.menu.open   {  display: block;}/* JS: toggle class, document click-outside,   keydown ESC, aria-expanded, aria-hidden */
Modern
/* <button popovertarget="menu"> ... <div popover> */#menu[popover]   {  position: absolute;  margin: 0.25rem 0;}/* Toggle, light dismiss, ESC: built in. */

CSS Modal Dialog with the Native dialog Element

Widely available
Old way
.overlay   {  position: fixed;  inset: 0;  background: rgb(0 0 0 / .5);}/* JS: addEventListener click, keydown ESC, focus trap,   aria-hidden, body scroll lock, z-index stacking */
Modern
dialog   {  padding: 1rem;}dialog::backdrop   {  background: rgb(0 0 0 / .5);}/* JS: dialog.showModal(); dialog.close(); */

Other categories

ESC