{"id":166,"date":"2023-12-08T08:42:00","date_gmt":"2023-12-08T08:42:00","guid":{"rendered":"http:\/\/fem.flywheelsites.com\/?p=166"},"modified":"2023-12-11T16:51:11","modified_gmt":"2023-12-11T16:51:11","slug":"light-dom-only","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/light-dom-only\/","title":{"rendered":"Light-DOM-Only Web Components are Sweet"},"content":{"rendered":"\n<p>First: the Light DOM is just&#8230; the regular DOM. <\/p>\n\n\n\n<p>When people talk about native Web Components, the Shadow DOM comes up a lot. I have extremely mixed feelings about the Shadow DOM. On one hand, it&#8217;s a powerful scoping tool. For example, CSS applied inside the Shadow DOM doesn&#8217;t &#8220;leak&#8221; outside, meaning you can be freer with naming and not worry about selector conflicts. Also, JavaScript from the outside can&#8217;t reach in, meaning a <code>querySelectorAll<\/code> isn&#8217;t going to select things inside the Shadow DOM. It&#8217;s a protective barrier that is unique to Web Components. No library can offer this, and that&#8217;s cool.<\/p>\n\n\n\n<p>That&#8217;s what the Shadow DOM <em>is.<\/em> What it <em>isn&#8217;t<\/em> is required for &#8220;HTML abstraction&#8221;, which I feel like it gets confused for. It had me confused for a while, anyway. If one of your goals for a Web Component is to hide away the HTML implementation details of a component, perhaps for ease-of-use, well, that&#8217;s great, but you can do that with the Shadow DOM <em>or<\/em> the Light DOM.<\/p>\n\n\n\n<p>Let&#8217;s get into this.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Guts of a Custom Element Can Become Light DOM<\/h2>\n\n\n\n<p>Here&#8217;s a custom element defined as <code>alert-machine<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" 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\">alert-machine<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span>&gt;<\/span>I'm a button.<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">alert-machine<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><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>In order for that to do anything, we need to execute some JavaScript that defines that element and its functionality:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">AlertMachine<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">HTMLElement<\/span> <\/span>{\n  connectedCallback() {\n    <span class=\"hljs-keyword\">this<\/span>.querySelector(<span class=\"hljs-string\">\"button\"<\/span>).addEventListener(<span class=\"hljs-string\">\"click\"<\/span>, () =&gt; {\n      alert(<span class=\"hljs-string\">\"Alert machine strikes again.\"<\/span>);\n    });\n  }\n}\n\ncustomElements.define(<span class=\"hljs-string\">\"alert-machine\"<\/span>, AlertMachine);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>I kinda prefer the slightly more elaborate setup with a constructor that allows for methods and such:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">AlertMachine<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">HTMLElement<\/span> <\/span>{\n  <span class=\"hljs-keyword\">constructor<\/span>() {\n    <span class=\"hljs-keyword\">super<\/span>();\n  }\n\n  connectedCallback() {\n    <span class=\"hljs-keyword\">this<\/span>.querySelector(<span class=\"hljs-string\">\"button\"<\/span>).addEventListener(<span class=\"hljs-string\">\"click\"<\/span>, <span class=\"hljs-keyword\">this<\/span>.doAlert);\n  }\n\n  doAlert() {\n    alert(<span class=\"hljs-string\">\"Alert machine strikes again.\"<\/span>);\n  }\n}\n\ncustomElements.define(<span class=\"hljs-string\">\"alert-machine\"<\/span>, AlertMachine);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Even if this is the first time you&#8217;re ever seeing Web Components code like this, you can make sense of it. <\/p>\n\n\n\n<p>What I like about what is happening so far is that <code>&lt;button&gt;<\/code> in the HTML is going to render on the page just like&#8230; a&#8230; <code>&lt;button&gt;<\/code> in HTML. It&#8217;s 100% just like that:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>It renders like a <code>&lt;button&gt;<\/code><\/li>\n\n\n\n<li>You can select it and style it from regular CSS with <code>button { }<\/code><\/li>\n\n\n\n<li>You can <code>querySelector<\/code> it from regular JavaScript<\/li>\n<\/ol>\n\n\n\n<p>It&#8217;s no different than any other HTML on the page. <\/p>\n\n\n\n<p>But notice the JavaScript <em>inside<\/em> the Web Component is adding a little extra functionality. That&#8217;s it. Neat.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_PoVpZob\" src=\"\/\/codepen.io\/anon\/embed\/PoVpZob?height=500&amp;theme-id=47434&amp;slug-hash=PoVpZob&amp;default-tab=js,result\" height=\"500\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed PoVpZob\" title=\"CodePen Embed PoVpZob\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">You Can Augment or Replace the HTML with Whatever<\/h2>\n\n\n\n<p>At this point, you might think: <\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><em>OK, whatever is inside the custom element is the Light DOM<\/em>. And:<\/li>\n\n\n\n<li><em>Well, that&#8217;s pretty limiting. I don&#8217;t want to have to be in charge of adding in all the needed HTML for every single component. That doesn&#8217;t bring very much value.<\/em><\/li>\n<\/ol>\n\n\n\n<p>That&#8217;s kinda true, but it doesn&#8217;t have to be the entire truth. You can totally wipe that away and inject your own HTML. You could keep that HTML but add more, wrapping things or whatever you need to do. Or you could just keep it as is. This is really no different than the possibilities with Shadow DOM. Do what you gotta do. You do lose the ability to use <code>&lt;slot \/&gt;<\/code>, which is pretty unfortunate, and I wish wasn&#8217;t true, but alas.<\/p>\n\n\n\n<p>So if your goal is to abstract away a bunch of HTML to make your component easier to use, go for it!<\/p>\n\n\n\n<p>But you probably should get on board with delivering a good set of HTML within your custom elements right in the delivered HTML. For one, that&#8217;s the fallback when JavaScript fails to load or run, which matters. But it&#8217;s not just a &#8220;fallback&#8221; or progressive enhancement technique entirely, it&#8217;s what is required for SSR (server-side rendering) which is also a performance concern and I think we all generally agree is a good idea.<\/p>\n\n\n\n<p>While I think the possibility that <em>not<\/em> having a build process for Web Components is attractive, if what the build process buys you is good, solid, HTML, then it&#8217;s probably worth it. (I&#8217;m mostly thinking of <a href=\"https:\/\/github.com\/11ty\/webc\">WebC<\/a> and <a href=\"https:\/\/enhance.dev\/\">Enhance<\/a> here).<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" 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\">my-component<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"card\"<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span>&gt;<\/span>Good, solid, renderable, accessible, non-embarassing HTML in here.<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">my-component<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><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>Take, for example, <a href=\"https:\/\/cloudfour.com\/thinks\/building-an-accessible-image-comparison-web-component\/\">the Image Comparison Slider<\/a> that Cloud Four put out as a Web Component:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" 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\">image-compare<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> <span class=\"hljs-attr\">alt<\/span>=<span class=\"hljs-string\">\"Alt Text\"<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"path\/to\/image.jpg\"<\/span> \/&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> <span class=\"hljs-attr\">alt<\/span>=<span class=\"hljs-string\">\"Alt text\"<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"path\/to\/image.jpg\"<\/span> \/&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">image-compare<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><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>Just two images! That&#8217;s a perfect fallback for this, because two images are exactly what you&#8217;re trying to show. But when the comparison slider loads, it&#8217;s got all sorts of other HTML that make it do the interactive stuff. That&#8217;s perfect. <\/p>\n\n\n\n<p>Their slider <em>does<\/em> use the Shadow DOM, which is fine of course. And actually, they use <code>&lt;slot \/&gt;<\/code> to put the images into place in the abstracted HTML is pretty useful. But they didn&#8217;t <em>have<\/em> to, this all could be done in the Light DOM. It would be a little extra work producing the final HTML, but a hell of a lot easier for consumers to style.<\/p>\n\n\n\n<p><a href=\"https:\/\/blog.jim-nielsen.com\/2023\/html-web-components\/\">Jim Nielsen has a nice way of saying it<\/a>:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>This feature of web components&nbsp;<a href=\"https:\/\/blog.jim-nielsen.com\/2023\/as-good-as-html\/\">encourages a design of composability<\/a>. Rather than an empty \u201cshell component\u201d that takes data and (using JavaScript exclusively) renders the entirety of its contents, web components encourage an approach of composing core content with HTML and then wrapping it in a custom element that enhances its contents with additional functionality.<\/p>\n<\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\">I don&#8217;t like styling the Shadow DOM<\/h2>\n\n\n\n<p>That&#8217;s kind of the rub here, for me. The main reason I&#8217;m so hot on Light DOM is that I find the styling story of Web Components using Shadow DOM annoying. <\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Styling very specific things with <code>::part<\/code> is a very niche styling thing and to me, not a real styling solution.<\/li>\n\n\n\n<li>Styling by documenting somewhere that certain CSS <code>--custom-properties<\/code> are in use is also limited and not a real styling solution. <\/li>\n\n\n\n<li>Styling by injecting a <code>&lt;style&gt;<\/code> tag into some template literal in the JavaScript itself feels awkward and ad hoc to me and I&#8217;m not a fan. <\/li>\n\n\n\n<li>Styling with an adopted stylesheet means an additional web request for each component or back to the template literal thing which is either awkward or slow. <\/li>\n<\/ul>\n\n\n\n<p>I don&#8217;t dislike that these options exist, I just don&#8217;t&#8230; like them. I&#8217;d rather be able to use the best styling API ever: regular CSS. There is some hope here, the idea of <a href=\"https:\/\/github.com\/WICG\/webcomponents\/issues\/909\">&#8220;open stylable shadow roots&#8221;<\/a> might take hold.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">So what are we giving up again?<\/h2>\n\n\n\n<p>If you go all-Light-DOM with a Web Component, you lose:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Encapsulation<\/li>\n\n\n\n<li>Slots<\/li>\n<\/ul>\n\n\n\n<p>You gain the ability to use regular ol&#8217; CSS from the parent page to style the thing. And, perhaps, a stronger reminder that Web Components should have good default HTML.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What People Are Saying About Light DOM<\/h2>\n\n\n\n<p>They certainly resonate with Eric Meyer!<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>I&nbsp;<em>like<\/em>&nbsp;the Light DOM.&nbsp; It\u2019s designed to work together pretty well.&nbsp; This whole high-fantasy-flavored Shadowlands of the DOM thing just doesn\u2019t sit right with me.<\/p>\n\n\n\n<p>If they do for you, that\u2019s great!&nbsp; Rock on with your bad self.&nbsp; I say all this mostly to set the stage for why I only recently had a breakthrough using web components, and now I quite like them.&nbsp; But not the shadow kind.&nbsp; I\u2019m talking about Fully Light-DOM Components here.<\/p>\n<cite><a href=\"https:\/\/meyerweb.com\/eric\/thoughts\/2023\/11\/01\/blinded-by-the-light-dom\/\">Blinded By the Light DOM<\/a><\/cite><\/blockquote>\n\n\n\n<p>Some baby bear just-right porridge from Jeremy Keith:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Dave talks about how web components can be&nbsp;<a href=\"https:\/\/daverupert.com\/2021\/10\/html-with-superpowers\/\">HTML with superpowers<\/a>. I think that\u2019s a good attitude to have. Instead of all-singing, all-dancing web components, it feels a lot more elegant to use web components to augment your existing markup with just enough extra behaviour.<\/p>\n\n\n\n<p>Where does the shadow DOM come into all of this? It doesn\u2019t. And that\u2019s okay. I\u2019m not saying it should be avoided completely, but it should be a last resort. See how far you can get with the composibility of regular HTML first.<\/p>\n<cite><a href=\"https:\/\/adactio.com\/journal\/20618\">HTML web components<br><\/a><\/cite><\/blockquote>\n\n\n\n<p>Mayank has a pretty hardline stance, and gets into similar problems I have with styling.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>I\u2019ve previously said&nbsp;<a href=\"https:\/\/www.mayank.co\/blog\/web-components-are-not-components#shadow-dom-is-not-fit-for-production-use\">\u201cshadow DOM is not fit for production use\u201d<\/a>, a statement which attracted a surprising amount of heat. Maybe I\u2019m asking for too much, but I would think that every respectable production-grade application has core needs \u2014 like accessibility, form participation, and the ability to work without JavaScript.<\/p>\n\n\n\n<p>Today though, I want to touch a little bit on the&nbsp;<strong>styling<\/strong>&nbsp;side of things.<\/p>\n<cite><a href=\"https:\/\/www.mayank.co\/blog\/presentational-shadow-dom\/\">Presentational shadow DOM<br><\/a><\/cite><\/blockquote>\n\n\n\n<p>Jim Neilsen used Light DOM only, found it useful, and even felt weird about it (<em>which you should not, Jim<\/em>)!<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Maybe I shouldn\u2019t be using the term \u201cweb component\u201d for what I\u2019ve done here. I\u2019m not using shadow DOM. I\u2019m not using the templates or slots. I\u2019m really only using&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Web_components\/Using_custom_elements\">custom elements<\/a>&nbsp;to attach functionality to a specific kind of component.<\/p>\n\n\n\n<p>But it still kinda feels like web components. All of this could\u2019ve been accomplished with regular ole\u2019 web techniques<\/p>\n<cite><a href=\"https:\/\/blog.jim-nielsen.com\/2023\/web-components-icon-galleries\/\">Using Web Components on My Icon Galleries Websites<\/a><\/cite><\/blockquote>\n\n\n\n<p>Adam Stoddard is into it:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>No shadow DOM, no templates, just the regular old DOM, which we now get to call the much cooler sounding \u201clight DOM\u201d.<\/p>\n<\/blockquote>\n\n\n\n<p>Adam specifically call out how cool the <code>connectedCallback<\/code> is. Whenever a custom element appears in the DOM it essentially auto-instantiates itself, which is an &#8220;unsung hero&#8221; of Web Components. Dave Rupert&#8217;s <a href=\"https:\/\/daverupert.com\/2023\/10\/fitvids-has-a-web-component-now\/\">simple and useful <code>&lt;fit-vids&gt;<\/code><\/a> has <a href=\"https:\/\/github.com\/davatron5000\/fit-vids\/blob\/main\/fit-vids.js\">no Shadow DOM in sight<\/a>, it just applies a few styles to what it finds in the Light DOM, but another reason to use it is how it automatically applies the styles when it shows up in the DOM. If you were to use the old school fitvids.js library, you would have to re-call the library if new videos were injected after it ran the first time.<\/p>\n\n\n\n<p>I&#8217;ll end with Miriam Suzanne:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Let the light&nbsp;DOM&nbsp;handle content wherever possible.<\/p>\n<cite><a href=\"https:\/\/www.oddbird.net\/2023\/11\/17\/components\/\">HTML&nbsp;Web Components are Just JavaScript?<\/a><\/cite><\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>How about y&#8217;all? How you feeling about the Light DOM approach?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>First: the Light DOM is just&#8230; the regular DOM. When people talk about native Web Components, the Shadow DOM comes up a lot. I have extremely mixed feelings about the Shadow DOM. On one hand, it&#8217;s a powerful scoping tool. For example, CSS applied inside the Shadow DOM doesn&#8217;t &#8220;leak&#8221; outside, meaning you can be [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":189,"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":[3,37,36],"class_list":["post-166","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-javascript","tag-light-dom","tag-web-components"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2023\/11\/light-dom-featured.jpg?fit=1000%2C500&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/166","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=166"}],"version-history":[{"count":16,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/166\/revisions"}],"predecessor-version":[{"id":255,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/166\/revisions\/255"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/189"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=166"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=166"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=166"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}