Skip to content

Bits of .NET

Daily micro-tips for C#, SQL, performance, and scalable backend engineering.

  • Asp.Net Core
  • C#
  • SQL
  • JavaScript
  • CSS
  • About
  • ErcanOPAK.com
  • No Access
  • Privacy Policy
HTML

Use HTML Dialog Element for Accessible Modals (Forget JavaScript Libraries)

- 01.02.26 | 01.02.26 - ErcanOPAK

Still using Bootstrap modals or custom JavaScript? The native <dialog> element handles accessibility, focus trapping, and backdrop clicks automatically.

The Complete HTML:

<!-- The dialog element -->
<dialog id="myModal">
  <form method="dialog">
    <h2>Confirm Action</h2>
    <p>Are you sure you want to delete this item?</p>
    
    <div class="actions">
      <button value="cancel">Cancel</button>
      <button value="confirm" autofocus>Delete</button>
    </div>
  </form>
</dialog>

<!-- Trigger button -->
<button onclick="document.getElementById('myModal').showModal()">
  Delete Item
</button>

The JavaScript (Minimal):

const dialog = document.getElementById('myModal');

// Listen for dialog close
dialog.addEventListener('close', () => {
  const returnValue = dialog.returnValue;
  
  if (returnValue === 'confirm') {
    console.log('User confirmed!');
    // Perform delete action
  } else {
    console.log('User cancelled');
  }
});

// Close on backdrop click
dialog.addEventListener('click', (e) => {
  if (e.target === dialog) {
    dialog.close('cancel');
  }
});

What You Get for Free:
✅ Focus trapping (Tab cycles only within dialog)
✅ Esc key closes the dialog
✅ Backdrop element (no separate div needed)
✅ ARIA attributes (role=”dialog”, aria-modal=”true”)
✅ Screen reader announcement
✅ Return value from form submission
✅ Stack management (multiple dialogs work correctly)

The CSS:

dialog {
  padding: 2rem;
  border: none;
  border-radius: 8px;
  box-shadow: 0 4px 16px rgba(0,0,0,0.3);
  max-width: 500px;
  width: 90%;
}

/* The backdrop */
dialog::backdrop {
  background: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(3px);
}

/* Animation */
dialog[open] {
  animation: slide-up 0.3s ease-out;
}

@keyframes slide-up {
  from {
    opacity: 0;
    transform: translateY(30px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Close animation */
dialog.closing {
  animation: slide-down 0.3s ease-in forwards;
}

@keyframes slide-down {
  to {
    opacity: 0;
    transform: translateY(30px);
  }
}

Advanced – Prevent Accidental Close:

const dialog = document.getElementById('myModal');

dialog.addEventListener('cancel', (e) => {
  // User pressed Esc
  if (hasUnsavedChanges()) {
    e.preventDefault(); // Block the close
    
    if (confirm('You have unsaved changes. Close anyway?')) {
      dialog.close('cancel');
    }
  }
});

Comparison with Bootstrap Modal:

// Bootstrap Modal
<!-- 50 lines of HTML -->
<!-- Requires Bootstrap JS: 60KB -->
<!-- Requires jQuery: 85KB -->
<!-- Total: 145KB + HTML complexity -->

// Native <dialog>
<!-- 10 lines of HTML -->
<!-- 15 lines of JavaScript -->
<!-- No libraries -->
<!-- Total: 0KB dependencies -->

Browser Support:
✅ Chrome 37+
✅ Edge 79+
✅ Firefox 98+
✅ Safari 15.4+

That’s 98%+ of all browsers. For older browsers, there’s a 2KB polyfill.

Pro Tip – Nested Dialogs:

<dialog id="outer">
  <h2>Main Dialog</h2>
  <button onclick="document.getElementById('inner').showModal()">
    Open Nested
  </button>
  
  <dialog id="inner">
    <h3>Nested Dialog</h3>
    <button onclick="this.closest('dialog').close()">Close</button>
  </dialog>
</dialog>

The browser automatically manages the dialog stack. Closing the inner dialog returns focus to the outer one. No manual tracking needed.

Related posts:

HTML5 Videos Fail on Mobile Browsers

How to align inconsistently sized logos with CSS

HTML Forms Submit Slower Than Expected

Post Views: 2

Post navigation

Create Smooth Page Transitions with CSS View Transitions API (No JavaScript)
Debounce vs Throttle in JavaScript: When to Use Which (With Real Examples)

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

February 2026
M T W T F S S
 1
2345678
9101112131415
16171819202122
232425262728  
« Jan    

Most Viewed Posts

  • Get the User Name and Domain Name from an Email Address in SQL (935)
  • How to add default value for Entity Framework migrations for DateTime and Bool (831)
  • Get the First and Last Word from a String or Sentence in SQL (825)
  • How to select distinct rows in a datatable in C# (799)
  • How to make theater mode the default for Youtube (714)
  • Add Constraint to SQL Table to ensure email contains @ (573)
  • How to enable, disable and check if Service Broker is enabled on a database in SQL Server (552)
  • Average of all values in a column that are not zero in SQL (519)
  • How to use Map Mode for Vertical Scroll Mode in Visual Studio (474)
  • Find numbers with more than two decimal places in SQL (436)

Recent Posts

  • C#: Use ArgumentNullException.ThrowIfNull for Cleaner Validation
  • C#: Use Discard Pattern to Ignore Unwanted Values
  • C#: Use Deconstruction with Tuples for Cleaner Multiple Returns
  • C#: Use File-Scoped Types to Limit Class Visibility
  • SQL: Use PIVOT to Transform Rows into Columns
  • SQL: Use MERGE OUTPUT to Track What Changed During Upsert
  • .NET Core: Use Polly for Resilient HTTP Requests with Retry Logic
  • .NET Core: Use Dapper for Lightweight ORM Alternative to Entity Framework
  • Git: Use git sparse-checkout to Clone Only Specific Folders
  • Git: Use git switch and git restore Instead of Confusing git checkout

Most Viewed Posts

  • Get the User Name and Domain Name from an Email Address in SQL (935)
  • How to add default value for Entity Framework migrations for DateTime and Bool (831)
  • Get the First and Last Word from a String or Sentence in SQL (825)
  • How to select distinct rows in a datatable in C# (799)
  • How to make theater mode the default for Youtube (714)

Recent Posts

  • C#: Use ArgumentNullException.ThrowIfNull for Cleaner Validation
  • C#: Use Discard Pattern to Ignore Unwanted Values
  • C#: Use Deconstruction with Tuples for Cleaner Multiple Returns
  • C#: Use File-Scoped Types to Limit Class Visibility
  • SQL: Use PIVOT to Transform Rows into Columns

Social

  • ErcanOPAK.com
  • GoodReads
  • LetterBoxD
  • Linkedin
  • The Blog
  • Twitter
© 2026 Bits of .NET | Built with Xblog Plus free WordPress theme by wpthemespace.com