{"id":9651,"date":"2026-06-01T09:08:42","date_gmt":"2026-06-01T14:08:42","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=9651"},"modified":"2026-06-01T09:08:44","modified_gmt":"2026-06-01T14:08:44","slug":"in-n-out-animations-dialogs-part-1-3","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/in-n-out-animations-dialogs-part-1-3\/","title":{"rendered":"In-N-Out Animations: Dialogs (Part 1\/3)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">I&#8217;d like to kick off a small series here focused on animating elements in and out of view. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">First, we&#8217;re going to focus on an element that goes from <code>display: none;<\/code> to <code>display: block;<\/code>. This is of particular interest because, well, it used to be <em>quite difficult<\/em> to do. But more than that: it&#8217;s often highly desirable. Movement can help a user understand what&#8217;s going when a new element appears or disappears.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s start with the modern wonder that is the <code>&lt;dialog><\/code> element.<\/p>\n\n\n\n<p class=\"learn-more wp-block-paragraph\">Just want the final code snippet?! <a href=\"#just-snippet\">Jump here.<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">By default, a <code>&lt;dialog&gt;<\/code> is <code>display: none;<\/code> and when you open it, it becomes <code>display: block;<\/code> naturally. We don&#8217;t have to write these styles. If we&#8217;re animating <em>something else<\/em> between these values, that&#8217;s fine, you&#8217;ll just need to do it yourself:<\/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-class\">.thing<\/span> {\n  <span class=\"hljs-attribute\">display<\/span>: none;\n  \n  &amp;.open {\n    <span class=\"hljs-attribute\">display<\/span>: block;\n  }\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 class=\"wp-block-paragraph\">If you&#8217;re like me, that triggers something in your brain that says <em>well now you can&#8217;t animate it, bud. <\/em> And indeed, if you tried like this:<\/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-class\">.thing<\/span> {\n  <span class=\"hljs-attribute\">display<\/span>: none;\n  <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">0<\/span>;\n  <span class=\"hljs-attribute\">transition<\/span>: <span class=\"hljs-number\">1s<\/span> opacity,\n\n  &amp;.open {\n    display: block;\n    <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">1<\/span>;\n  }\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 class=\"wp-block-paragraph\">That transition would indeed <em>not<\/em> work. If you toggled that <code>open<\/code> class, the element would immediately appear (and disappear).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s swap over to <code>&lt;dialog><\/code> styles, as that&#8217;s what we&#8217;ll be animating the rest of the time.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" 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\">opacity<\/span>: <span class=\"hljs-number\">0<\/span>;\n  <span class=\"hljs-attribute\">transition<\/span>: <span class=\"hljs-number\">1s<\/span> opacity,\n\n  &amp;:open {\n    opacity: <span class=\"hljs-number\">1<\/span>;\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><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 class=\"wp-block-paragraph\">(Note the difference of not hand-writing the <code>display<\/code> change and using the <code>:open<\/code> pseudo-class.)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here&#8217;s a video of where we are at:<\/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: 405px;\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe loading=\"lazy\" title=\"VideoPress Video Player\" aria-label=\"VideoPress Video Player\" width=\"500\" height=\"657\" src=\"https:\/\/videopress.com\/embed\/WlCIjiRc?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=1770107250'><\/script><\/div>\n\t\t\t<figcaption>No animation at all, yet.<\/figcaption>\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<h2 class=\"wp-block-heading\">Introducing <code>allow-discrete<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The first trick we&#8217;re going to need to employ is using a special keyword as the <code>transition-behavior<\/code>. Here it is:<\/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-selector-tag\">dialog<\/span> {\n  <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">0<\/span>;\n  <span class=\"hljs-attribute\">transition<\/span>: \n    <span class=\"hljs-number\">1s<\/span> opacity,\n    <span class=\"hljs-number\">1s<\/span> display allow-discrete;\n\n  &amp;:open {\n    <span class=\"hljs-comment\">\/* as it first renders, \n       it's already this. *\/<\/span>\n    <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">1<\/span>;\n  }\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 class=\"wp-block-paragraph\">I&#8217;m applying the <code>transition-behavior<\/code> as part of the shorthand value there. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I&#8217;m sure that name, <code>allow-discrete<\/code>, has some fancy reason for being named that, but I don&#8217;t know what that is, and I find the name <em>pretty rough<\/em> (it doesn&#8217;t help me understand it). But we need it, so c&#8217;est la vie.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The problem <\/strong>we were facing (with the lack of animation) is that the display property changes at an inopportune time during the timeline. We want to change it such that when we&#8217;re changing <em>to<\/em> <code>display: block;<\/code> we want that to happen <em>right away<\/em> then let the <code>opacity<\/code> transition (in our case). Vice versa, when we&#8217;re changing to <code>display: none;<\/code> we want it to <em>wait<\/em> to change until the transition is over. Here&#8217;s a diagram of that:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"402\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/Screenshot-2026-05-28-at-5.23.52-PM.png?resize=1024%2C402&#038;ssl=1\" alt=\"\" class=\"wp-image-9859\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/Screenshot-2026-05-28-at-5.23.52-PM.png?resize=1024%2C402&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/Screenshot-2026-05-28-at-5.23.52-PM.png?resize=300%2C118&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/Screenshot-2026-05-28-at-5.23.52-PM.png?resize=768%2C301&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/Screenshot-2026-05-28-at-5.23.52-PM.png?resize=1536%2C602&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/Screenshot-2026-05-28-at-5.23.52-PM.png?resize=2048%2C803&amp;ssl=1 2048w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">With that in place, well, here&#8217;s a movie of what happens:<\/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: 418px;\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe loading=\"lazy\" title=\"VideoPress Video Player\" aria-label=\"VideoPress Video Player\" width=\"500\" height=\"592\" src=\"https:\/\/videopress.com\/embed\/rG8ZAAja?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=1770107250'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s still&#8230; not great.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s be clear here about our goals and how we&#8217;re doing so far:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u274c Dialog instantly appears<br>\u274c Backdrop instantly&nbsp;appears<br>\u2705 Dialog fades out<br>\u274c Backdrop instantly hides<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So 1 out of 4. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We&#8217;ve got more work to do. Let&#8217;s get that dialog <em>fading in.<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Using @starting-style<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The way I understand the dialog <em>not<\/em> fading in, so far, is that when the dialog renders on the page <em>for the very first time<\/em>, the <code>:open<\/code> styles immediately apply to it, which are <code>display: block<\/code> and <code>opacity: 1<\/code> already, so there is no need\/time to animate anything. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So we need to be very specific with styles for that pre-animation state. One way to do that is applying a <code>@keyframe<\/code> animation and letting it run to completion, which will happen when the element first renders. But I don&#8217;t love that way because it feels more like trickery than an explicit choice to me, and more importantly, the animation isn&#8217;t &#8220;interuptable&#8221; (<a href=\"https:\/\/codepen.io\/phaux\/pen\/RNrPNgG\">see explanation in code here)<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The way to be specific about &#8220;before open&#8221; styles is a CSS things called <code>@starting-style<\/code>. It looks like this:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs shcb-code-table\"><span class='shcb-loc'><span>dialog {\n<\/span><\/span><span class='shcb-loc'><span>  opacity: 0;\n<\/span><\/span><span class='shcb-loc'><span>  transition: \n<\/span><\/span><span class='shcb-loc'><span>    1s opacity,\n<\/span><\/span><span class='shcb-loc'><span>    1s display allow-discrete;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  &amp;:open {\n<\/span><\/span><span class='shcb-loc'><span>    opacity: 1;\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>  \n<\/span><\/span><mark class='shcb-loc'><span>  @starting-style {\n<\/span><\/mark><span class='shcb-loc'><span>    &amp;:open {\n<\/span><\/span><span class='shcb-loc'><span>      opacity: 0;\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">This is basically saying, hey, when you <em>first render<\/em> on the page, it&#8217;s actually got <em>these<\/em> styles, and if there happens to be any animation\/transition applied, they will happen from <em>these<\/em> <em>values.<\/em> Which is exactly what we need.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That gets us here:<\/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: 484px;\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe loading=\"lazy\" title=\"VideoPress Video Player\" aria-label=\"VideoPress Video Player\" width=\"500\" height=\"600\" src=\"https:\/\/videopress.com\/embed\/OnlkVzFT?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=1770107250'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p class=\"wp-block-paragraph\">A bit better. We&#8217;ve checked another one off the list:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u2705&nbsp;Dialog fades in<br>\u274c Backdrop instantly&nbsp;appears<br>\u2705 Dialog fades out<br>\u274c Backdrop instantly hides<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We just haven&#8217;t dealt with that backdrop yet, and you can really feel it. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But before we go there, we need to take some stock into what we&#8217;re doing here. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">@starting-style is LAST<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Notice I&#8217;ve put the styles for <code>@starting-style<\/code> at the end of our block of code styling the dialog element.<\/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-tag\">dialog<\/span> {\n  <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">0<\/span>;\n  <span class=\"hljs-attribute\">transition<\/span>: \n    <span class=\"hljs-number\">1s<\/span> opacity,\n    <span class=\"hljs-number\">1s<\/span> display allow-discrete;\n\n  &amp;:open {\n    <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">1<\/span>;\n  }\n  \n  <span class=\"hljs-keyword\">@starting-style<\/span> {\n    &amp;<span class=\"hljs-selector-pseudo\">:open<\/span> {\n      <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">0<\/span>;\n    }\n  }\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 class=\"wp-block-paragraph\">That&#8217;s very on purpose. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Those styles <em>need to override<\/em> the <code>:open<\/code> styles, but <code>@starting-style<\/code> doesn&#8217;t add any specificity, so they must come later in order to successfully override. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Closed Styles<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A bit of a strange thing has emerged here in that the styles that are directly applied to <code>dialog<\/code> end up being the styles when the <code>dialog<\/code> isn&#8217;t open. Meaning when it comes to animation, the &#8220;on the way out&#8221; styling, or the styles that the animation moves to as the dialog is being closed. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We can group those styles more clearly, like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-selector-tag\">dialog<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attribute\">transition<\/span>: \n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-number\">1s<\/span> opacity,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-number\">1s<\/span> display allow-discrete;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><mark class='shcb-loc'><span>  &amp;:not(:open) {\n<\/span><\/mark><span class='shcb-loc'><span>    <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">0<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  &amp;<span class=\"hljs-selector-pseudo\">:open<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">1<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>  \n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">@starting-style<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>    &amp;<span class=\"hljs-selector-pseudo\">:open<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">0<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><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<h2 class=\"wp-block-heading\">The *Three State* System<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Now we have the styles for this dialog isolated into three distinct chunks. <\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>The &#8220;On The Way Out&#8221; Styles<\/li>\n\n\n\n<li>The &#8220;Open&#8221; Styles<\/li>\n\n\n\n<li>The &#8220;On The Way In&#8221; Styles<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s how they are in the source order. But if we&#8217;re going to re-number them as the user would experience them, I&#8217;d actually <em>reverse<\/em> the order.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"622\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/06\/Screenshot-2026-06-01-at-6.10.20-AM.png?resize=1024%2C622&#038;ssl=1\" alt=\"\" class=\"wp-image-9894\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/06\/Screenshot-2026-06-01-at-6.10.20-AM.png?resize=1024%2C622&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/06\/Screenshot-2026-06-01-at-6.10.20-AM.png?resize=300%2C182&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/06\/Screenshot-2026-06-01-at-6.10.20-AM.png?resize=768%2C467&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/06\/Screenshot-2026-06-01-at-6.10.20-AM.png?resize=1536%2C933&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/06\/Screenshot-2026-06-01-at-6.10.20-AM.png?resize=2048%2C1244&amp;ssl=1 2048w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The process of opening-then-closing the dialog is like this illustration below, hence the &#8220;backwards&#8221; ordering:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"541\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/06\/Screenshot-2026-06-01-at-6.12.35-AM.png?resize=1024%2C541&#038;ssl=1\" alt=\"\" class=\"wp-image-9895\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/06\/Screenshot-2026-06-01-at-6.12.35-AM-scaled.png?resize=1024%2C541&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/06\/Screenshot-2026-06-01-at-6.12.35-AM-scaled.png?resize=300%2C158&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/06\/Screenshot-2026-06-01-at-6.12.35-AM-scaled.png?resize=768%2C405&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/06\/Screenshot-2026-06-01-at-6.12.35-AM-scaled.png?resize=1536%2C811&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/06\/Screenshot-2026-06-01-at-6.12.35-AM-scaled.png?resize=2048%2C1081&amp;ssl=1 2048w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n\n<p class=\"learn-more wp-block-paragraph\">If you remember anything from this post, I think remembering the three-state system is the most important. When dealing with in-and-out styling, you have the opportunity to style all three states, and it&#8217;s best to put them in reverse source order.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Three States is Cool!<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Styling all three states here is all but a requirement. But instead of having it feel like a repetitive burden, think of it as an opportunity to do some unique design work. Is the &#8220;on the way in&#8221; style <em>bigger<\/em> (like <code>scale: 1.1;<\/code>)?, then maybe the the &#8220;on the way out&#8221; style could be smaller (like <code>scale: 0.9;<\/code>). It could <a href=\"https:\/\/codepen.io\/editor\/chriscoyier\/pen\/019c9184-da1d-76bf-b87b-2cb41fdff184\">even have different anchor points<\/a>! <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Backdrop<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In order to accomplish our four goals, we still need to deal with the styling behind the dialog, known as the backdrop, and selected in CSS with <code>::backdrop<\/code>. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There is a lot to get right here:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>The <code>::backdrop<\/code> needs it&#8217;s own transitions<\/li>\n\n\n\n<li>It needs <em>another<\/em> special keyword<\/li>\n\n\n\n<li>It needs all three states of styles added<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Keeping it simple and only dealing with an opacity fade in\/out, it looks like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-selector-tag\">dialog<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attribute\">transition<\/span>: \n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-number\">1s<\/span> opacity,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-number\">1s<\/span> display allow-discrete,\n<\/span><\/span><mark class='shcb-loc'><span>    <span class=\"hljs-number\">1s<\/span> overlay allow-discrete;\n<\/span><\/mark><span class='shcb-loc'><span>\n<\/span><\/span><mark class='shcb-loc'><span>  &amp;::backdrop {\n<\/span><\/mark><mark class='shcb-loc'><span>    <span class=\"hljs-attribute\">transition<\/span>: opacity <span class=\"hljs-number\">1s<\/span>;\n<\/span><\/mark><mark class='shcb-loc'><span>  }\n<\/span><\/mark><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  &amp;<span class=\"hljs-selector-pseudo\">:not(<\/span><span class=\"hljs-selector-pseudo\">:open)<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">0<\/span>;\n<\/span><\/span><mark class='shcb-loc'><span>    &amp;::backdrop {\n<\/span><\/mark><mark class='shcb-loc'><span>      <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">0<\/span>;\n<\/span><\/mark><mark class='shcb-loc'><span>    }\n<\/span><\/mark><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  &amp;<span class=\"hljs-selector-pseudo\">:open<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">1<\/span>;\n<\/span><\/span><mark class='shcb-loc'><span>    &amp;::backdrop {\n<\/span><\/mark><mark class='shcb-loc'><span>      <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">1<\/span>;\n<\/span><\/mark><mark class='shcb-loc'><span>    }\n<\/span><\/mark><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>  \n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">@starting-style<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>    &amp;<span class=\"hljs-selector-pseudo\">:open<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">0<\/span>;\n<\/span><\/span><mark class='shcb-loc'><span>      &amp;::backdrop {\n<\/span><\/mark><mark class='shcb-loc'><span>        <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">0<\/span>;\n<\/span><\/mark><mark class='shcb-loc'><span>      }\n<\/span><\/mark><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><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 class=\"wp-block-paragraph\">Notice we&#8217;re now applying a <code>transition<\/code> to the <code>overlay<\/code> property which<em>&#8230; isn&#8217;t actually a property?<\/em> <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>overlay<\/code> keyword is another thing that I can&#8217;t say I fully get. It has something to do with enabling &#8220;top layer&#8221; animations, of which <code>dialog<\/code> and <code>::backdrop<\/code> are a part. I&#8217;m not a fan, as it feels like something quite random and obscure that doesn&#8217;t quite jive with how other things work in CSS. If you don&#8217;t include it here, the <code>::backdrop<\/code> will not animate out smoothly. But note that we need to apply it to the <code>dialog<\/code>, not the <code>::backdrop<\/code> itself even though it only affects the <code>::backdrop<\/code>. \ud83e\udd37\u200d\u2640\ufe0f. I did sit in a CSSWG meeting where they were discussing removing it, so we&#8217;ll see.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">With all this in place: we&#8217;re in business:<\/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: 556px;\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe loading=\"lazy\" title=\"VideoPress Video Player\" aria-label=\"VideoPress Video Player\" width=\"500\" height=\"652\" src=\"https:\/\/videopress.com\/embed\/fpGPXjJX?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=1770107250'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p class=\"wp-block-paragraph\">\u2705\u00a0Dialog fades in<br>\u2705\u00a0Backdrop fades in<br>\u2705 Dialog fades out<br>\u2705\u00a0Backdrop fades out<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Demo<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Allow me to add a few more basic styles so it feels a bit more real, and tada:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_019dcbb7-bf98-72cc-9850-b14b1b27972b\" src=\"\/\/codepen.io\/editor\/anon\/embed\/019dcbb7-bf98-72cc-9850-b14b1b27972b?height=652&amp;theme-id=1&amp;slug-hash=019dcbb7-bf98-72cc-9850-b14b1b27972b&amp;default-tab=css,result\" height=\"652\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed 019dcbb7-bf98-72cc-9850-b14b1b27972b\" title=\"CodePen Embed 019dcbb7-bf98-72cc-9850-b14b1b27972b\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Reduced Motion<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Note that on this demo, we&#8217;ve got an <code>@media<\/code> query to accommodate users who prefer reduced motion:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@media<\/span> (<span class=\"hljs-attribute\">prefers-reduced-motion:<\/span> reduce) {\n  <span class=\"hljs-selector-tag\">dialog<\/span> {  \n    <span class=\"hljs-attribute\">transition<\/span>:\n    <span class=\"hljs-number\">1s<\/span> opacity,\n    <span class=\"hljs-number\">1s<\/span> display allow-discrete,\n    <span class=\"hljs-number\">1s<\/span> overlay allow-discrete;\n\n    &amp;, &amp;:open {\n      <span class=\"hljs-attribute\">translate<\/span>: <span class=\"hljs-number\">0<\/span>;\n    }\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><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 class=\"wp-block-paragraph\">I&#8217;ve <em>leaving in the opacity<\/em> animation, just removing the actual <em>movement<\/em>, which I think is the proper spirit of this user setting.<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe loading=\"lazy\" title=\"VideoPress Video Player\" aria-label=\"VideoPress Video Player\" width=\"500\" height=\"417\" src=\"https:\/\/videopress.com\/embed\/IyoLoViB?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=1770107250'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<h2 id=\"just-snippet\" class=\"wp-block-heading\">Just The Snippet<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Looking for a starting point to copy and paste? Here ya go:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" 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\">transition<\/span>:\n    <span class=\"hljs-number\">1s<\/span> opacity,\n    <span class=\"hljs-number\">1s<\/span> display allow-discrete,\n    <span class=\"hljs-number\">1s<\/span> overlay allow-discrete;\n  \n  &amp;::backdrop {\n    <span class=\"hljs-attribute\">transition<\/span>:\n      opacity <span class=\"hljs-number\">1s<\/span>,\n      display <span class=\"hljs-number\">1s<\/span> allow-discrete,\n      overlay <span class=\"hljs-number\">1s<\/span> allow-discrete;\n  }\n\n  &amp;<span class=\"hljs-selector-pseudo\">:not(<\/span><span class=\"hljs-selector-pseudo\">:open)<\/span> {\n    <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">0<\/span>;\n    \n    &amp;::backdrop {\n      <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">0<\/span>;\n    }\n  }\n  \n  &amp;<span class=\"hljs-selector-pseudo\">:open<\/span> {\n    <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">1<\/span>;\n    \n    &amp;::backdrop {\n      <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">1<\/span>;\n    }\n  }\n\n  <span class=\"hljs-keyword\">@starting-style<\/span> {\n    &amp;<span class=\"hljs-selector-pseudo\">:open<\/span> {\n      <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">0<\/span>;\n\n      &amp;::backdrop {\n        <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">0<\/span>;\n      }\n    }\n  }\n}\n\n<span class=\"hljs-keyword\">@media<\/span> (<span class=\"hljs-attribute\">prefers-reduced-motion:<\/span> reduce) {\n  <span class=\"hljs-selector-tag\">dialog<\/span> {\n    <span class=\"hljs-comment\">\/* If you add animation that isn't just opacity, remember to remove it here. *\/<\/span>\n    <span class=\"hljs-comment\">\/* transition:\n      1s opacity,\n      1s display allow-discrete,\n      1s overlay allow-discrete; *\/<\/span>\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><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<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I feel like you get it. Three states. Style them all. Get all the little details right, including the source order, so it actually works. The reason I wrote this post is that it&#8217;s actually kinda easy to get those details <em>wrong<\/em> and have something not animate that you think should be. And because of the history with these features, it not working can feel like a hard limitation, like maybe it just <em>can&#8217;t<\/em> work. Well, it can. Hopefully these demos and explanations can help.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">And one more thing.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wouldn&#8217;t it be kinda cool to be able to abstract this kind of thing away with a CSS <code>@mixin<\/code> or something? I think that is still being <a href=\"https:\/\/drafts.csswg.org\/css-mixins-1\/#mixin-rule\">shaken out<\/a>, so while that&#8217;s true, I&#8217;ll just make up some fake code I think would be cool. I won&#8217;t include how the <code>@mixin<\/code> would be written, just how I&#8217;d want to <em>use<\/em> it once it has, and how this would expand into our final snippet is an exercise for authors.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@mixin<\/span> --in-<span class=\"hljs-keyword\">and<\/span>-out-animation(\n  <span class=\"hljs-attribute\">--transition-properties:<\/span> &lt;array&gt;,\n  <span class=\"hljs-attribute\">--in-style:<\/span> &lt;style-block&gt;,\n  <span class=\"hljs-attribute\">--open-style:<\/span> &lt;style-block&gt;,\n  <span class=\"hljs-attribute\">--out-style:<\/span> &lt;style-block&gt;,\n  <span class=\"hljs-attribute\">--timing:<\/span> &lt;duration&gt;,\n  <span class=\"hljs-attribute\">--backdrop:<\/span> &lt;boolean&gt;\n) {\n  ... <span class=\"hljs-selector-tag\">magic<\/span> ...\n}\n\n<span class=\"hljs-selector-tag\">dialog<\/span> {\n  @apply --in-and-out-animation(\n    <span class=\"hljs-attribute\">--transition-properties<\/span>: opacity, translate, scale,\n    --in-style: {\n      opacity: <span class=\"hljs-number\">0<\/span>;\n      <span class=\"hljs-attribute\">translate<\/span>: <span class=\"hljs-number\">100px<\/span> <span class=\"hljs-number\">0<\/span>;\n      <span class=\"hljs-attribute\">scale<\/span>: <span class=\"hljs-number\">1.1<\/span>;\n    }\n    <span class=\"hljs-selector-tag\">--open-style<\/span> {\n      <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">1<\/span>;\n      <span class=\"hljs-attribute\">translate<\/span>: <span class=\"hljs-number\">0<\/span>;\n      <span class=\"hljs-attribute\">scale<\/span>: <span class=\"hljs-number\">1<\/span>;\n    }\n    <span class=\"hljs-selector-tag\">--out-style<\/span>: {\n      <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">0<\/span>;\n      <span class=\"hljs-attribute\">translate<\/span>: -<span class=\"hljs-number\">100px<\/span> <span class=\"hljs-number\">0<\/span>;\n      <span class=\"hljs-attribute\">scale<\/span>: <span class=\"hljs-number\">0.9<\/span>;\n    }\n    <span class=\"hljs-selector-tag\">--timing<\/span>: 350<span class=\"hljs-selector-tag\">ms<\/span>;\n    <span class=\"hljs-selector-tag\">--backdrop<\/span>: <span class=\"hljs-selector-tag\">true<\/span>;\n  );\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><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>","protected":false},"excerpt":{"rendered":"<p>You can style the &#8220;on the way in&#8221; and &#8220;on the way out&#8221; styles for elements, even when they are moving to\/from display: none;. Yay.<\/p>\n","protected":false},"author":1,"featured_media":9901,"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":[490,100,7,98,491,350],"class_list":["post-9651","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-allow-discrete","tag-animation","tag-css","tag-dialog","tag-overlay","tag-prefers-reduced-motion"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/06\/in-and-out.jpg?fit=1734%2C1003&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/9651","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=9651"}],"version-history":[{"count":8,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/9651\/revisions"}],"predecessor-version":[{"id":9904,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/9651\/revisions\/9904"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/9901"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=9651"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=9651"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=9651"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}