<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ajay D</title>
    <description>The latest articles on DEV Community by Ajay D (@ajay_dharmaraj).</description>
    <link>https://dev.to/ajay_dharmaraj</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1514246%2F4f9762bf-286b-48bb-b4a6-122e21962e48.png</url>
      <title>DEV Community: Ajay D</title>
      <link>https://dev.to/ajay_dharmaraj</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ajay_dharmaraj"/>
    <language>en</language>
    <item>
      <title>Native HTML Series Part 3: Popover API</title>
      <dc:creator>Ajay D</dc:creator>
      <pubDate>Sat, 28 Feb 2026 13:15:45 +0000</pubDate>
      <link>https://dev.to/ajay_dharmaraj/native-html-series-part-3-popover-api-1mm</link>
      <guid>https://dev.to/ajay_dharmaraj/native-html-series-part-3-popover-api-1mm</guid>
      <description>&lt;h2&gt;
  
  
  Native HTML Series Part 3: Tooltips and Menus with the Popover API
&lt;/h2&gt;

&lt;p&gt;Welcome to Part 3 of our series on replacing third-party UI libraries with native HTML. We’ve already covered zero-JS accordions and native modals. Today, we are looking at the newest tool in the browser's arsenal: the Popover API.&lt;/p&gt;

&lt;p&gt;Think about how often you build tooltips, user profile dropdowns, or custom toast notifications. Historically, getting these to float above your content without &lt;code&gt;z-index&lt;/code&gt; bugs—while also remembering to close them when the user clicks somewhere else—required a dedicated JavaScript library or writing complex event listeners.&lt;/p&gt;

&lt;p&gt;The new &lt;code&gt;popover&lt;/code&gt; attribute solves all of this natively.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bare Bones
&lt;/h2&gt;

&lt;p&gt;Unlike &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;, which is a specific HTML tag, &lt;code&gt;popover&lt;/code&gt; is a global attribute. You can attach it to a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;, an &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt;, or any other element to instantly turn it into a floating popup.&lt;/p&gt;

&lt;p&gt;Here is the entire HTML required to create a working popover menu, with absolutely zero JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;popovertarget=&lt;/span&gt;&lt;span class="s"&gt;"user-menu"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Open User Menu&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"user-menu"&lt;/span&gt; &lt;span class="na"&gt;popover&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Profile&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Settings&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Sign Out&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. By linking the button's &lt;code&gt;popovertarget&lt;/code&gt; to the &lt;code&gt;id&lt;/code&gt; of the div, the browser handles the click events completely behind the scenes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Magic: Built-In Light Dismiss
&lt;/h2&gt;

&lt;p&gt;In Part 2, we had to write a JavaScript function to close our modal when the user clicked the background.&lt;/p&gt;

&lt;p&gt;With the Popover API, this behavior (known as "light dismiss") is built-in by default. When a popover is open, doing any of the following will automatically close it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clicking anywhere outside the popover.&lt;/li&gt;
&lt;li&gt;Pressing the &lt;code&gt;Escape&lt;/code&gt; key.&lt;/li&gt;
&lt;li&gt;Opening another popover.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Furthermore, just like the &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element, popovers are promoted to the browser's "Top Layer," guaranteeing they will always sit on top of your other content, regardless of the parent container's &lt;code&gt;overflow: hidden&lt;/code&gt; or &lt;code&gt;z-index&lt;/code&gt; rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  The CSS Glow-Up
&lt;/h2&gt;

&lt;p&gt;By default, an open popover is just centered in the middle of the screen. We can use CSS to style it as a sleek, floating menu and add transitions using the &lt;code&gt;:popover-open&lt;/code&gt; pseudo-class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Style the popover box */&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;popover&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#e2e8f0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt; &lt;span class="m"&gt;15px&lt;/span&gt; &lt;span class="m"&gt;-3px&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c"&gt;/* Basic positioning - reset defaults */&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;250px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Remove default list styling for our menu */&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;popover&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;list-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;popover&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#f1f5f9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;popover&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="nd"&gt;:last-child&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: For true relative positioning (e.g., anchoring the popover exactly below the button), you would use the new CSS Anchor Positioning API, which pairs perfectly with popovers but is a topic for a future post!&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;The Popover API is a massive win for developer experience. It standardizes one of the most common UI patterns on the web. It means less JavaScript sent to the client, fewer accessibility bugs, and a much cleaner DOM. If you are writing end-to-end tests with tools like Playwright, testing these interactions becomes a breeze because the browser's native state is completely predictable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interactive Demo:&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;(Click the button below to see the popover in action. Notice how clicking anywhere else on the screen instantly closes it—no JS required!)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;

&lt;iframe height="600" src="https://codepen.io/AjayD-007/embed/xbEGWoe?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;


&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>html</category>
      <category>tutorial</category>
      <category>ui</category>
    </item>
    <item>
      <title>Native HTML Series Part 2: &lt;dialog&gt; Element</title>
      <dc:creator>Ajay D</dc:creator>
      <pubDate>Thu, 26 Feb 2026 06:59:11 +0000</pubDate>
      <link>https://dev.to/ajay_dharmaraj/native-html-series-part-2-element-1jp2</link>
      <guid>https://dev.to/ajay_dharmaraj/native-html-series-part-2-element-1jp2</guid>
      <description>&lt;p&gt;Welcome to Part 2 of our series on replacing heavy JavaScript UI components with native HTML. In [Part 1] &lt;em&gt;(link to previous post)&lt;/em&gt;, we built a zero-JS accordion. Today, we are tackling a notorious front-end headache: the modal.&lt;/p&gt;

&lt;p&gt;Building a custom modal used to mean fighting with CSS &lt;code&gt;z-index&lt;/code&gt;, manually trapping the user's keyboard focus so they couldn't tab behind the modal, and writing script after script to handle closing it when they hit the &lt;code&gt;Escape&lt;/code&gt; key.&lt;/p&gt;

&lt;p&gt;Now, we have the native &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Bare Bones
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element represents a semantic popup window. While you do use a tiny bit of JavaScript to open it, the browser handles the complex accessibility and stacking context for you.&lt;/p&gt;

&lt;p&gt;Here is the markup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"openBtn"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Open Modal&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;dialog&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"myModal"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Native Modal Magic&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Notice how you can't click the background? The browser handles the focus trap automatically.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"dialog"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Close&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dialog&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To open this, we just need one tiny JavaScript event listener:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;modal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myModal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;openBtn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;openBtn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;openBtn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showModal&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Opens as a modal (blocks the rest of the page)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Magic: &lt;code&gt;showModal()&lt;/code&gt; vs. &lt;code&gt;show()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The real power of the &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element lies in the &lt;code&gt;.showModal()&lt;/code&gt; method. When you use this method (instead of just &lt;code&gt;.show()&lt;/code&gt;), the browser does three incredible things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Top Layer:&lt;/strong&gt; It promotes the dialog to a special browser "top layer" that exists outside the normal HTML document flow. You will never have a &lt;code&gt;z-index&lt;/code&gt; conflict again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus Trapping:&lt;/strong&gt; It automatically prevents the user from tabbing to elements behind the modal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Escape Key:&lt;/strong&gt; It wires up the &lt;code&gt;Esc&lt;/code&gt; key to close the modal natively. No event listeners required.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The CSS Glow-Up: The &lt;code&gt;::backdrop&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;When opened with &lt;code&gt;.showModal()&lt;/code&gt;, the browser inserts a pseudo-element behind the dialog called &lt;code&gt;::backdrop&lt;/code&gt;. This lets us easily dim or blur the rest of the page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Style the modal box itself */&lt;/span&gt;
&lt;span class="nt"&gt;dialog&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt; &lt;span class="m"&gt;25px&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Style the background overlay */&lt;/span&gt;
&lt;span class="nt"&gt;dialog&lt;/span&gt;&lt;span class="nd"&gt;::backdrop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;backdrop-filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;blur&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c"&gt;/* Adds a nice frosted glass effect */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Zero-JS Close Button
&lt;/h3&gt;

&lt;p&gt;Notice the &lt;code&gt;&amp;lt;form method="dialog"&amp;gt;&lt;/code&gt; in the HTML snippet above? This is a brilliant feature. If a button inside a form with &lt;code&gt;method="dialog"&lt;/code&gt; is clicked, the browser automatically closes the dialog. It completely eliminates the need to write an &lt;code&gt;addEventListener&lt;/code&gt; just to wire up your close buttons.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Missing Piece: Clicking Outside to Close
&lt;/h3&gt;

&lt;p&gt;There is one UX expectation the &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element doesn't handle automatically: clicking the dim backdrop to close the modal (often called a "light dismiss").&lt;/p&gt;

&lt;p&gt;Fortunately, because the &lt;code&gt;::backdrop&lt;/code&gt; is technically part of the &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element itself, we can solve this with a very clever, three-line JavaScript trick:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Close the modal when clicking outside of it&lt;/span&gt;
&lt;span class="nx"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you click the backdrop, the &lt;code&gt;event.target&lt;/code&gt; is the dialog itself. If you click the white box of the modal, the target is the child element. This snippet perfectly handles the "click outside" expectation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why This Matters
&lt;/h3&gt;

&lt;p&gt;Relying on native elements doesn't just reduce your bundle size; it drastically simplifies your codebase. As an added bonus, it makes end-to-end testing incredibly straightforward. If you are writing UI tests with tools like Playwright, you no longer have to wait for custom JS animations or write complex assertions for custom ARIA states—the DOM provides the absolute source of truth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interactive Demo:&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;(Interact with the native dialog below! Notice how you can use the Esc key, the close button, or click the background to dismiss it.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;

&lt;iframe height="600" src="https://codepen.io/AjayD-007/embed/XJjWvMJ?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;


&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>html</category>
      <category>tutorial</category>
      <category>ui</category>
    </item>
    <item>
      <title>Native HTML Series Part 1: Building Accordions</title>
      <dc:creator>Ajay D</dc:creator>
      <pubDate>Tue, 24 Feb 2026 12:05:13 +0000</pubDate>
      <link>https://dev.to/ajay_dharmaraj/native-html-series-part-1-building-accordions-58m6</link>
      <guid>https://dev.to/ajay_dharmaraj/native-html-series-part-1-building-accordions-58m6</guid>
      <description>&lt;h2&gt;
  
  
  Building Accordions with &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Welcome to the first post in a series where we explore native HTML features that can replace heavy JavaScript libraries. As front-end developers, we often reach for third-party packages to build common UI components. But modern browsers have evolved, and many of these interactive elements are now built right into the DOM.&lt;/p&gt;

&lt;p&gt;Today, we're starting simple: the accordion.&lt;/p&gt;

&lt;p&gt;Instead of writing custom state management and manually toggling ARIA attributes, we can use the native &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt; tags to build an accessible, functional accordion with zero JavaScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bare Bones
&lt;/h2&gt;

&lt;p&gt;At its core, the &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; element represents a disclosure widget from which the user can retrieve additional information. The &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt; element acts as the clickable heading for that widget.&lt;/p&gt;

&lt;p&gt;Here is the absolute minimum HTML required:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;details&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;Click to expand&lt;span class="nt"&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Here is the hidden content! The browser handles the toggle state and accessibility announcements automatically.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/details&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, the browser renders a small triangle next to the summary text. Clicking the summary toggles the &lt;code&gt;open&lt;/code&gt; attribute on the &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; tag, showing or hiding the content inside.&lt;/p&gt;

&lt;h3&gt;
  
  
  The CSS Glow-Up
&lt;/h3&gt;

&lt;p&gt;The default browser styling is functional but rarely fits a modern design system. Fortunately, customizing it is straightforward.&lt;/p&gt;

&lt;p&gt;First, we usually want to remove the default triangle marker so we can add our own custom icons (like a plus/minus or a custom chevron).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Remove the default triangle and layout the bar */&lt;/span&gt;
&lt;span class="nt"&gt;summary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;list-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* Works in most modern browsers */&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f4f4f5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c"&gt;/* NEW: Flexbox aligns the text and our new arrow */&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;space-between&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Specifically for Safari */&lt;/span&gt;
&lt;span class="nt"&gt;summary&lt;/span&gt;&lt;span class="nd"&gt;::-webkit-details-marker&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* NEW: Create the custom arrow */&lt;/span&gt;
&lt;span class="nt"&gt;summary&lt;/span&gt;&lt;span class="nd"&gt;::after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"›"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* A simple chevron character */&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&gt;/* Add a smooth animation for when it rotates */&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="m"&gt;0.3s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;/* Style the content box */&lt;/span&gt;
&lt;span class="nt"&gt;details&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;details&lt;/span&gt; &lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#f4f4f5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the browser adds an &lt;code&gt;open&lt;/code&gt; attribute to the &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; tag when it's expanded, we can use CSS attribute selectors to style the open state without touching JavaScript.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Change the background color when open */&lt;/span&gt;
&lt;span class="nt"&gt;details&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;open&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nt"&gt;summary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e4e4e7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-bottom-left-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-bottom-right-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Modern Superpower: The &lt;code&gt;name&lt;/code&gt; Attribute
&lt;/h3&gt;

&lt;p&gt;Historically, if you wanted an "exclusive" accordion—where opening one section automatically closes the others—you &lt;em&gt;had&lt;/em&gt; to write JavaScript to listen for click events and manage the states.&lt;/p&gt;

&lt;p&gt;Recently, browsers introduced support for the &lt;code&gt;name&lt;/code&gt; attribute on the &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; element. By giving multiple &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; tags the exact same &lt;code&gt;name&lt;/code&gt;, you group them together. The browser will natively ensure that only one element in that group is open at a time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;details&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"faq-accordion"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;What is HTML5?&lt;span class="nt"&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;The latest evolution of the standard that defines HTML.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/details&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;details&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"faq-accordion"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;Do I need JavaScript for this?&lt;span class="nt"&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Not anymore! The name attribute handles the exclusive toggling natively.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/details&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why This Matters
&lt;/h3&gt;

&lt;p&gt;Relying on native elements doesn't just reduce your bundle size; it drastically simplifies your codebase. As an added bonus, it makes end-to-end testing incredibly straightforward. If you are writing UI tests with tools like Playwright, you no longer have to wait for custom JS animations or write complex assertions for custom ARIA states—the DOM provides the absolute source of truth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interactive Demo:&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;(Below is a live CodePen demonstrating the exclusive accordion with custom CSS. Feel free to interact with it or tweak the code!)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;

&lt;iframe height="600" src="https://codepen.io/AjayD-007/embed/myrdEXR?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;


&lt;/p&gt;

</description>
      <category>html</category>
      <category>accordian</category>
      <category>beginners</category>
      <category>css</category>
    </item>
  </channel>
</rss>
