{"id":5578,"date":"2025-04-16T16:54:36","date_gmt":"2025-04-16T21:54:36","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=5578"},"modified":"2025-05-26T14:57:11","modified_gmt":"2025-05-26T19:57:11","slug":"lessons-learned-from-recreating-a-styled-dialog","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/lessons-learned-from-recreating-a-styled-dialog\/","title":{"rendered":"Lessons Learned from Recreating a Styled Dialog"},"content":{"rendered":"\n<p>I was on the epicgames.com website the other day, signing up so I could relive my Magic: The Gathering glory days with Arena. While doing that I saw their style for modal dialogs and thought <em>I should try to re-create that with <code>&lt;dialog&gt;<\/code><\/em> because apparently I&#8217;m both of those types of nerd.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"900\" height=\"1024\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/04\/Screenshot-2025-04-14-at-8.20.15%E2%80%AFAM.png?resize=900%2C1024&#038;ssl=1\" alt=\"\" class=\"wp-image-5580\" style=\"width:415px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/04\/Screenshot-2025-04-14-at-8.20.15%E2%80%AFAM.png?resize=900%2C1024&amp;ssl=1 900w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/04\/Screenshot-2025-04-14-at-8.20.15%E2%80%AFAM.png?resize=264%2C300&amp;ssl=1 264w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/04\/Screenshot-2025-04-14-at-8.20.15%E2%80%AFAM.png?resize=768%2C874&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/04\/Screenshot-2025-04-14-at-8.20.15%E2%80%AFAM.png?w=1310&amp;ssl=1 1310w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\">It&#8217;s a <code>&lt;dialog&gt;<\/code><\/h2>\n\n\n\n<p>This thing came up <em>on top<\/em> of other content, so that alone makes it appropriate for the HTML <code>&lt;dialog&gt;<\/code> element. We&#8217;ll use that.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">No Taller than Viewport<\/h2>\n\n\n\n<p>It&#8217;s not absolutely required that the entire <code>&lt;dialog&gt;<\/code> needs to be shorter than the viewport. If it&#8217;s taller, you just need to be able to scroll to all of the content it contains. The default styling for <code>&lt;dialog&gt;<\/code> allows for that. <\/p>\n\n\n\n<p>But I would argue that if you&#8217;re putting actions that relate to the content of the dialog <em>at the bottom<\/em> then you <em>should<\/em> limit the height of the dialog to the viewport so that those actions are always visible. If a dialog simply has an \u2715 close button on the top, maybe it doesn&#8217;t matter, but here we&#8217;ve got important buttons at the bottom, so it does. <\/p>\n\n\n\n<p>The default styling for dialog includes <code>position: absolute;<\/code> and we can keep that while limiting the height like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">dialog<\/span> {\n  <span class=\"hljs-attribute\">block-size<\/span>: <span class=\"hljs-number\">90<\/span>dvb;\n  <span class=\"hljs-attribute\">inset-block-start<\/span>: <span class=\"hljs-number\">5<\/span>dvb;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>That will limit the height to essentially 90% of the viewport height (the <code>dv<\/code>b part means &#8220;<strong>d<\/strong>ynamic <strong>v<\/strong>iewport size in the <strong>b<\/strong>lock direction&#8221;). I like the &#8220;dynamic&#8221; sizing units because it means that it accommodates browser &#8220;chrome&#8221; (toolbars and stuff) being present (or not). The inset amount is half of what&#8217;s left over, so essentially vertical centering.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/04\/100dvh-adapts-itself-be-91c728b09836d_1920.png?resize=1024%2C576&#038;ssl=1\" alt=\"\" class=\"wp-image-5589\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/04\/100dvh-adapts-itself-be-91c728b09836d_1920.png?resize=1024%2C576&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/04\/100dvh-adapts-itself-be-91c728b09836d_1920.png?resize=300%2C169&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/04\/100dvh-adapts-itself-be-91c728b09836d_1920.png?resize=768%2C432&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/04\/100dvh-adapts-itself-be-91c728b09836d_1920.png?resize=1536%2C864&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/04\/100dvh-adapts-itself-be-91c728b09836d_1920.png?resize=2048%2C1152&amp;ssl=1 2048w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/04\/100dvh-adapts-itself-be-91c728b09836d_1920.png?w=3000&amp;ssl=1 3000w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">This graphic convinces me dynamic viewport height units are a good idea. (<a href=\"https:\/\/web.dev\/blog\/viewport-units\">source<\/a>)<\/figcaption><\/figure>\n\n\n\n<p class=\"learn-more\">Note that the dialog element&#8217;s default styles can be a bit confusing and you need to understand when you can override safely and when you can&#8217;t without doing extra work. Simon Willison has an interesting article on this: <a href=\"https:\/\/til.simonwillison.net\/css\/dialog-full-height\">Styling an HTML dialog modal to take the full height of the viewport<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Limited Width and Centering<\/h2>\n\n\n\n<p>This example has lots of written content in it (a bunch of <code>&lt;p&gt;<\/code>s) so it&#8217;s best practice to limit the width to a readable line length. When that&#8217;s the intent, it&#8217;s nice to use the <code>ch<\/code> unit as it maps roughly to number of characters, which is what we&#8217;re trying to limit. <\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">dialog<\/span> {\n  ...\n\n  <span class=\"hljs-attribute\">inline-size<\/span>: <span class=\"hljs-built_in\">min<\/span>(<span class=\"hljs-number\">50ch<\/span>, <span class=\"hljs-number\">90<\/span>dvi);\n  <span class=\"hljs-attribute\">margin-inline<\/span>: auto;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Fifty characters of width is providing good readability here, but it&#8217;s possible that the current screen is actually narrower than that, hence the <code>min()<\/code> function assuring that the width will never be wider than 90% of the viewport. I&#8217;m not sure if our fancy dancy &#8220;dynamic viewport units in the inline direction&#8221; is buying us anything here, but it balances the usage with where we were using <code>dvb<\/code>). <\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"604\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/04\/Screenshot-2025-04-15-at-3.25.18%E2%80%AFPM-1.png?resize=1024%2C604&#038;ssl=1\" alt=\"\" class=\"wp-image-5595\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/04\/Screenshot-2025-04-15-at-3.25.18%E2%80%AFPM-1.png?resize=1024%2C604&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/04\/Screenshot-2025-04-15-at-3.25.18%E2%80%AFPM-1.png?resize=300%2C177&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/04\/Screenshot-2025-04-15-at-3.25.18%E2%80%AFPM-1.png?resize=768%2C453&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/04\/Screenshot-2025-04-15-at-3.25.18%E2%80%AFPM-1.png?resize=1536%2C906&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/04\/Screenshot-2025-04-15-at-3.25.18%E2%80%AFPM-1.png?resize=2048%2C1207&amp;ssl=1 2048w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"483\" height=\"1024\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/04\/Screenshot-2025-04-15-at-3.25.27%E2%80%AFPM.png?resize=483%2C1024&#038;ssl=1\" alt=\"\" class=\"wp-image-5592\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/04\/Screenshot-2025-04-15-at-3.25.27%E2%80%AFPM.png?resize=483%2C1024&amp;ssl=1 483w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/04\/Screenshot-2025-04-15-at-3.25.27%E2%80%AFPM.png?resize=141%2C300&amp;ssl=1 141w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/04\/Screenshot-2025-04-15-at-3.25.27%E2%80%AFPM.png?w=656&amp;ssl=1 656w\" sizes=\"auto, (max-width: 483px) 100vw, 483px\" \/><\/figure>\n<\/div>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Modal vs Non Modal (and the <code>open<\/code> attribute)<\/h2>\n\n\n\n<p>This seems like a pretty important distinction to know about:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>&#8220;Modal&#8221;<\/strong> (<code>dialog.showModal()<\/code>) means <em>interrupt everything else, this dialog needs to be dealt with immediately.<\/em>\n<ul class=\"wp-block-list\">\n<li>The ESC key automatically works to close it. <\/li>\n\n\n\n<li>Focus is put on the first focusable element within the dialog<\/li>\n\n\n\n<li>Focus is trapped within the dialog<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>&#8220;Non Modal&#8221;<\/strong> (<code>dialog.show()<\/code>) means the dialog is just <em>there<\/em>, but doesn&#8217;t require exclusive or immediate action.\n<ul class=\"wp-block-list\">\n<li>None of those other things above happen. You likely want to bind the ESC key yourself anyway.<\/li>\n\n\n\n<li>When you use the open attribute (useful when working on them!) like <code>&lt;dialog open&gt;<\/code> the dialog is open non-modally. <\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p>In our example, where a person needs to accept-or-not the Terms &amp; Conditions, it&#8217;s likely <strong>modal<em> <\/em><\/strong>is the better approach. That way what the person is trying to do can only continue if they accept or take a different path if they do not. This choice is likely required to know what to do next. <\/p>\n\n\n\n<p>A non-modal dialog implementation might be something like a &#8220;site navigation drawer&#8221; where some of the attributes of using a modal is desirable (e.g. the hide\/show behavior) but focus trapping is not required or even desirable.<\/p>\n\n\n\n<p>Here&#8217;s a video of focus trapping at work with the modal state. Notice the &#8220;focusable element&#8221; (an anchor link) never gets focus, because it&#8217;s not within the <code>&lt;dialog&gt;<\/code>. <\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player aligncenter wp-block-jetpack-videopress--has-max-width\" style=\"max-width: 525px;\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='461' src='https:\/\/videopress.com\/embed\/I7aZQ7Yy?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1739540970'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<h2 class=\"wp-block-heading\">No Invokers? Yes Invokers!<\/h2>\n\n\n\n<p><s>There is no way to show a dialog in the modal state from HTML alone.<\/s> <\/p>\n\n\n\n<p>Welllll, the above isn&#8217;t strictly true anymore as I <a href=\"https:\/\/frontendmasters.com\/blog\/lessons-learned-from-recreating-a-styled-dialog\/#comment-25883\">learned from Curtis Wilcox&nbsp;in the comments<\/a>. We can actually use the <code>popover<\/code> syntax to make a button in HTML alone that will open the dialog. That will (sadly) only open the dialog in the non-modal state, but at least it&#8217;s a toggle without JavaScript! The good news is that the Invoker Commands API is actually all over this. It&#8217;s used like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dialog<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"my-dialog\"<\/span> <span class=\"hljs-attr\">popover<\/span>&gt;<\/span>\n  ...\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dialog<\/span>&gt;<\/span>\n\n<span class=\"hljs-comment\">&lt;!-- \n  popovertarget is the fallback\n\n  command attributes are the new school,\n  which open in a modal state!\n--&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span>\n  <span class=\"hljs-attr\">popovertarget<\/span>=<span class=\"hljs-string\">\"my-dialog\"<\/span>\n\n  <span class=\"hljs-attr\">command<\/span>=<span class=\"hljs-string\">\"show-modal\"<\/span>\n  <span class=\"hljs-attr\">commandfor<\/span>=<span class=\"hljs-string\">\"my-dialog\"<\/span>\n&gt;<\/span>\n  Open Modal\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span>\n  <span class=\"hljs-attr\">popovertarget<\/span>=<span class=\"hljs-string\">\"my-dialog\"<\/span>\n  <span class=\"hljs-attr\">popovertargetaction<\/span>=<span class=\"hljs-string\">\"hide\"<\/span>\n\n  <span class=\"hljs-attr\">commandfor<\/span>=<span class=\"hljs-string\">\"my-dialog\"<\/span>\n  <span class=\"hljs-attr\">command<\/span>=<span class=\"hljs-string\">\"close\"<\/span>\n&gt;<\/span>\n  Close\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"learn-more\">To bone up: <a href=\"https:\/\/frontendmasters.com\/blog\/whats-the-difference-between-htmls-dialog-element-and-popovers\/\">What\u2019s the Difference Between HTML\u2019s Dialog Element and Popovers?<\/a><\/p>\n\n\n\n<p class=\"learn-more\"><a href=\"https:\/\/codepen.io\/chriscoyier\/pen\/GggQrQq\">This demo<\/a> has an example of trying to use both invokers and popovers before falling back to the JavaScript methods for opening\/closing.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Careful with the <code>display<\/code> value<\/h2>\n\n\n\n<p>The reason that <code>&lt;dialog&gt;<\/code> is invisible by default is simply that default styles render it with <code>display: none;<\/code>. That is precariously easy to override. In fact, in this very demo I was playing with, I wanted to use <code>display: flex;<\/code> on the dialog to have the header\/content\/footer look where the content is <code>flex: 1;<\/code> to push the header and footer away and take up the remaining space. But you&#8217;ll have problems like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-comment\">\/* Oops, dialog is always open *\/<\/span>\n<span class=\"hljs-selector-tag\">dialog<\/span> {\n  <span class=\"hljs-attribute\">display<\/span>: flex;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>It&#8217;s probably most resilient to just not mess with the <code>display<\/code> value of dialogs, instead using some internal wrapper element instead. But I&#8217;m a gamblin&#8217; man apparently so I did:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">dialog {\n  &amp;&#91;open] {\n    display: flex;\n  }\n}<\/code><\/span><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Trimming <code>margin<\/code> can come anytime now<\/h2>\n\n\n\n<p>Any time I slap a bunch of elements into a container (read: doing web design) I&#8217;m reminded that the block-direction margins are kind of annoying in that context. The last item, particularly if it&#8217;s content, will likely have margin at the end that pushes further than you want it away from the container, or the start, or both. <\/p>\n\n\n\n<p>It leads to this kind of thing:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">.container {\n  :first-child {\n    margin-block-start: 0;\n  }\n  :last-child {\n    margin-block-end: 0;\n  }\n}<\/code><\/span><\/pre>\n\n\n<p>When instead we could be living in the future like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.container<\/span> {\n  <span class=\"hljs-attribute\">margin-trim<\/span>: block;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>I once <a href=\"https:\/\/chriscoyier.net\/2023\/06\/12\/margin-trim-as-a-best-practice\/\">said this<\/a> and I&#8217;m sticking to it:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>If you add&nbsp;<code>padding<\/code>&nbsp;in the main flow direction of an element, adding&nbsp;<code>margin-trim<\/code>&nbsp;in that same direction.<\/p>\n<\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\">Right aligned buttons deux fa\u00e7ons<\/h2>\n\n\n\n<p>I had <a href=\"https:\/\/frontendmasters.com\/blog\/the-latest-in-the-how-are-we-going-to-do-masonry-debate-apple-says-item-flow\/\"><code>item-flow<\/code> on my brain<\/a> when I was tinkering with this and thinking about how flow directions can be reversed, which is something I don&#8217;t think about or use very much. For some reason when I needed to right-align those buttons for &#8220;Accept&#8221; and &#8220;Close&#8221;, my fingers went for:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">dialog {\n  &gt; footer {\n    display: flex;\n    flex-direction: row-reverse;\n  }\n}<\/code><\/span><\/pre>\n\n\n<p>I&#8217;m not going to recommend that, as it changes the tabbing order awkwardly for no great reason. You should probably just do:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">dialog {\n  &gt; footer {\n    display: flex;\n    justify-content: end;\n  }\n}<\/code><\/span><\/pre>\n\n\n<p>But, ya know, always nice to have options. You could also not even bother with <code>flex<\/code> and do <code>text-align: end<\/code> or even old school <code>float: right<\/code> the buttons.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Autofocus<\/h2>\n\n\n\n<p>In reading over <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Reference\/Elements\/dialog\">the MDN for dialogs<\/a>, this stood out to me as something I didn&#8217;t know:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>The&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Reference\/Global_attributes\/autofocus\"><code>autofocus<\/code><\/a>&nbsp;attribute should be added to the element the user is expected to interact with immediately upon opening a modal dialog. If no other element involves more immediate interaction, it is recommended to add&nbsp;<code>autofocus<\/code>&nbsp;to the close button inside the dialog, or the dialog itself if the user is expected to click\/activate it to dismiss.<\/p>\n<\/blockquote>\n\n\n\n<p>They didn&#8217;t mince words there and it makes sense to me, so I put it on the &#8220;Accept&#8221; button as that seems like the most likely user action. <\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dialog<\/span>&gt;<\/span>\n  ...\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">footer<\/span>&gt;<\/span>\n    ...\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">autofocus<\/span>&gt;<\/span>Accept<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">footer<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dialog<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>Feel free to <a href=\"https:\/\/codepen.io\/editor\/chriscoyier\/pen\/dPyExGZ\/ca8110591c1b7d7c8a41a51c9917b46a\">peak at the dxemo<\/a> to see a few other thing like color modes and a backdrop. Sometimes fairly simple looking HTML elements have quite a bit of detail to implementation!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Sometimes pretty simple HTML elements have a lot of things to consider and take care of, from interactivity, styling, accessibility, and more.<\/p>\n","protected":false},"author":1,"featured_media":5602,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[7,98,31],"class_list":["post-5578","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-css","tag-dialog","tag-html"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/04\/Lessons-Learned-from-Recreating-a-Styled-Dialog.jpg?fit=1140%2C676&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/5578","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=5578"}],"version-history":[{"count":17,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/5578\/revisions"}],"predecessor-version":[{"id":6003,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/5578\/revisions\/6003"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/5602"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=5578"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=5578"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=5578"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}