{"id":1650,"date":"2024-04-11T08:46:11","date_gmt":"2024-04-11T14:46:11","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=1650"},"modified":"2024-04-11T08:46:13","modified_gmt":"2024-04-11T14:46:13","slug":"a-css-powered-add-remove-tags-ui","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/a-css-powered-add-remove-tags-ui\/","title":{"rendered":"A CSS-Powered Add\/Remove Tags UI"},"content":{"rendered":"\n<p>Checkboxes and labels&nbsp;<em>used<\/em>&nbsp;to have to be right next to each other to be a potent UI duo. You could do trickery like this:<\/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\">label<\/span> <span class=\"hljs-attr\">for<\/span>=<span class=\"hljs-string\">\"toggle\"<\/span>&gt;<\/span>Toggle<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"checkbox\"<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"toggle\"<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/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<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-id\">#toggle<\/span><span class=\"hljs-selector-pseudo\">:checked<\/span> + <span class=\"hljs-selector-tag\">div<\/span> {\n  <span class=\"hljs-comment\">\/* Do something fancy in here with a toggleable on\/off state *\/<\/span>\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>But now, thanks to&nbsp;<code>:has()<\/code>&nbsp;in CSS, we\u2019re not beholden to that structure anymore.&nbsp;<a href=\"https:\/\/frontendmasters.com\/blog\/we-can-has-it-all\/\">We can :has() it all<\/a>, as it is said. Now that these HTML elements have some autonomy, without losing their connection to one another, a lot can be achieved<\/p>\n\n\n\n<p>Using this as a base concept, we can build a tag management component operated entirely in HTML &amp; CSS.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">A Tag Component with Interactive HTML<\/h2>\n\n\n\n<p>A&nbsp;<code>&lt;label&gt;<\/code>&nbsp;is an interactive element that can trigger its controlled element. For instance, when we click a&nbsp;<code>&lt;label&gt;<\/code>&nbsp;paired with an&nbsp;<code>&lt;input type=\"checkbox\"&gt;<\/code>&nbsp;the checkbox\u2019s&nbsp;<code>:checked<\/code>&nbsp;state toggles.<\/p>\n\n\n\n<p>A combo with a&nbsp;<code>&lt;label&gt;<\/code>&nbsp;allows us to design a toggle UI that can be operated from&nbsp;<em><strong>two different<\/strong><\/em>&nbsp;locations on a page (both the label and the checkbox). The controlled element is in one location and the label is in the other.<\/p>\n\n\n\n<p>How far and how independent these two locations have to be from each other for the duo to work used to be limited, since labels can\u2019t directly inform us which state their controlled element is currently in. For that, we have to ask the controlled element itself each time, and keep the label close to the element, so the label can be accessed in CSS during the element\u2019s state change.<\/p>\n\n\n\n<p>That was the case before.<\/p>\n\n\n\n<p>Because of modern CSS standards like Grid,&nbsp;<code>:has()<\/code>&nbsp;selector and such, there\u2019s much more freedom now between the source code arrangement in the HTML and our&nbsp;<a href=\"https:\/\/codepen.io\/chriscoyier\/pen\/xxejMyw\">ability to reach<\/a>&nbsp;<a href=\"https:\/\/codepen.io\/chriscoyier\/pen\/xxejMyw\">any<\/a>&nbsp;<a href=\"https:\/\/codepen.io\/chriscoyier\/pen\/xxejMyw\">element in CSS<\/a>.<\/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\">&lt;<span class=\"hljs-selector-tag\">div<\/span>&gt;\n  &lt;<span class=\"hljs-selector-tag\">label<\/span> <span class=\"hljs-selector-tag\">for<\/span>=\"<span class=\"hljs-selector-tag\">one<\/span>\"&gt;<span class=\"hljs-selector-tag\">One<\/span>&lt;\/<span class=\"hljs-selector-tag\">label<\/span>&gt;\n  &lt;<span class=\"hljs-selector-tag\">label<\/span> <span class=\"hljs-selector-tag\">for<\/span>=\"<span class=\"hljs-selector-tag\">two<\/span>\"&gt;<span class=\"hljs-selector-tag\">Two<\/span>&lt;\/<span class=\"hljs-selector-tag\">label<\/span>&gt;\n  &lt;<span class=\"hljs-selector-tag\">label<\/span> <span class=\"hljs-selector-tag\">for<\/span>=\"<span class=\"hljs-selector-tag\">three<\/span>\"&gt;<span class=\"hljs-selector-tag\">Three<\/span>&lt;\/<span class=\"hljs-selector-tag\">label<\/span>&gt;\n&lt;\/<span class=\"hljs-selector-tag\">div<\/span>&gt;\n\n&lt;<span class=\"hljs-selector-tag\">p<\/span>&gt;<span class=\"hljs-selector-tag\">Arbitrary<\/span> <span class=\"hljs-selector-tag\">DOM<\/span>&lt;\/<span class=\"hljs-selector-tag\">p<\/span>&gt;\n\n&lt;<span class=\"hljs-selector-tag\">div<\/span>&gt;\n  &lt;<span class=\"hljs-selector-tag\">input<\/span> <span class=\"hljs-selector-tag\">type<\/span>=\"<span class=\"hljs-selector-tag\">checkbox<\/span>\" <span class=\"hljs-selector-tag\">id<\/span>=\"<span class=\"hljs-selector-tag\">one<\/span>\"&gt;\n  &lt;<span class=\"hljs-selector-tag\">input<\/span> <span class=\"hljs-selector-tag\">type<\/span>=\"<span class=\"hljs-selector-tag\">checkbox<\/span>\" <span class=\"hljs-selector-tag\">id<\/span>=\"<span class=\"hljs-selector-tag\">two<\/span>\"&gt;\n  &lt;<span class=\"hljs-selector-tag\">input<\/span> <span class=\"hljs-selector-tag\">type<\/span>=\"<span class=\"hljs-selector-tag\">checkbox<\/span>\" <span class=\"hljs-selector-tag\">id<\/span>=\"<span class=\"hljs-selector-tag\">three<\/span>\"&gt;\n&lt;\/<span class=\"hljs-selector-tag\">div<\/span>&gt;<\/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<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\">body<\/span><span class=\"hljs-selector-pseudo\">:has(<\/span><span class=\"hljs-selector-id\">#one<\/span><span class=\"hljs-selector-pseudo\">:checked)<\/span> <span class=\"hljs-selector-tag\">p<\/span> {\n  <span class=\"hljs-attribute\">background<\/span>: pink;\n}\n<span class=\"hljs-selector-tag\">body<\/span><span class=\"hljs-selector-pseudo\">:has(<\/span><span class=\"hljs-selector-id\">#two<\/span><span class=\"hljs-selector-pseudo\">:checked)<\/span> <span class=\"hljs-selector-tag\">p<\/span> {\n  <span class=\"hljs-attribute\">background<\/span>: lightgreen;\n}\n<span class=\"hljs-selector-tag\">body<\/span><span class=\"hljs-selector-pseudo\">:has(<\/span><span class=\"hljs-selector-id\">#three<\/span><span class=\"hljs-selector-pseudo\">:checked)<\/span> <span class=\"hljs-selector-tag\">p<\/span> {\n  <span class=\"hljs-attribute\">background<\/span>: lightblue;\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>In this article,&nbsp;<strong>I\u2019ll be using checkboxes, labels<\/strong>,&nbsp;<strong>and<\/strong>&nbsp;<code><a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/:has\">:has()<\/a><\/code>&nbsp;<strong>selectors to design<\/strong>&nbsp;<strong>a UI where you can add and remove \u201ctags\u201d<\/strong>. The UI will have a set of tags to select from, and a set of tags that have been selected. Clicking a tag in one set removes it from one area and makes it appear in the other set. It\u2019s a functionality that\u2019s perfect for checkboxes and labels to take on. Using the&nbsp;<code>:has()<\/code>&nbsp;selector means, I can keep the two set of tags in as much of a distance or depth from each other as I want, which in turn provides a lot flexibility.<\/p>\n\n\n\n<p>Although the\u00a0<code>:has()<\/code>\u00a0selector can be used in many ways, in this article we\u2019ll focus on its ability to target an element containing a specific child element. The parent is mentioned before the colon (<code>:<\/code>) and the child is mentioned inside the parentheses of\u00a0<code>has()<\/code>. For example,\u00a0<code>p:has(>\u00a0mark)<\/code> selects all elements that have at least one direct descendant that\u2019s a <code>&lt;mark><\/code>. Another example, <code>div:has(:checked)<\/code> selects all <code>&lt;div><\/code> elements that have at least one descendant (direct or not) element that\u2019s in a checked state, like a radio or checkbox.<\/p>\n\n\n\n<p>Now that we\u2019ve got the basics covered. Here\u2019s the final demo we\u2019ll be working towards. We\u2019re going to use&nbsp;<strong>movie genres<\/strong>&nbsp;as our tags.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_WNWXvve\" src=\"\/\/codepen.io\/anon\/embed\/WNWXvve?height=450&amp;theme-id=47434&amp;slug-hash=WNWXvve&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed WNWXvve\" title=\"CodePen Embed WNWXvve\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>Let\u2019s get started.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">HTML Construction<\/h2>\n\n\n\n<p>There are two parts:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>One part nests the checkboxes<\/li>\n\n\n\n<li>The other, their labels<\/li>\n<\/ol>\n\n\n\n<p>Because we\u2019ll be designing a cluster of tags of movie genres, a script is set up to add the checkboxes and labels for each genre to the HTML.<\/p>\n\n\n\n<p>The script uses HTML&nbsp;<code><a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Element\/template\">&lt;template&gt;<\/a><\/code>&nbsp;to build the new elements off of. This is to prove that you could build all of this dynamically with arbitrary tags from a data source. You can use any method you prefer or not use script at all and directly build the HTML. You\u2019ll see the full source code in a moment.<\/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\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-comment\">&lt;!-- Plus tags (tags to be included) will render here --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ul<\/span>&gt;<\/span>\n  <span class=\"hljs-comment\">&lt;!-- Minus tags (tags already included) will render here --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">ul<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">template<\/span>&gt;<\/span>\n  <span class=\"hljs-comment\">&lt;!-- The tags' templates --&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"plus\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"checkbox\"<\/span> \/&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">li<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"minus\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">li<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">template<\/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<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> template = <span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">'template'<\/span>).content;\n\n<span class=\"hljs-keyword\">const<\/span> div = <span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">'div'<\/span>);\n<span class=\"hljs-keyword\">const<\/span> ul = <span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">'ul'<\/span>);\n\n<span class=\"hljs-keyword\">const<\/span> genres = &#91;<span class=\"hljs-string\">\"Adventure\"<\/span>, <span class=\"hljs-string\">\"Comedy\"<\/span>, <span class=\"hljs-string\">\"Thriller\"<\/span>, <span class=\"hljs-string\">\"Horror\"<\/span>];\n\n<span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">let<\/span> i = <span class=\"hljs-number\">0<\/span>; i &lt; genres.length; i++) {\n  <span class=\"hljs-comment\">\/* get a clone of the template's content *\/<\/span>\n  <span class=\"hljs-keyword\">let<\/span> clone = template.cloneNode(<span class=\"hljs-literal\">true<\/span>);\n\n  <span class=\"hljs-keyword\">let<\/span> checkbox = clone.querySelector(<span class=\"hljs-string\">'input&#91;type=\"checkbox\"]'<\/span>);\n  <span class=\"hljs-comment\">\/* add id to the checkbox *\/<\/span>\n  checkbox.setAttribute(<span class=\"hljs-string\">\"id\"<\/span>, <span class=\"hljs-string\">`c<span class=\"hljs-subst\">${i + <span class=\"hljs-number\">1<\/span>}<\/span>`<\/span>);\n  <span class=\"hljs-comment\">\/* add genre text to the plus tag *\/<\/span>\n  checkbox.parentElement.innerHTML += genres&#91;i];\n  <span class=\"hljs-comment\">\/* add plus tag to the div on page *\/<\/span>\n  div.appendChild(clone.querySelector(<span class=\"hljs-string\">':has(input&#91;type=\"checkbox\"])'<\/span>));\n\n  <span class=\"hljs-keyword\">let<\/span> label = clone.querySelector(<span class=\"hljs-string\">\"label\"<\/span>);\n  <span class=\"hljs-comment\">\/* add \"for\" attr. to the label *\/<\/span>\n  label.setAttribute(<span class=\"hljs-string\">\"for\"<\/span>, <span class=\"hljs-string\">`c<span class=\"hljs-subst\">${i + <span class=\"hljs-number\">1<\/span>}<\/span>`<\/span>);\n  <span class=\"hljs-comment\">\/* add text to the minus tag *\/<\/span>\n  label.innerText = genres&#91;i];\n  <span class=\"hljs-comment\">\/* add minus tag to the ul on page *\/<\/span>\n  ul.appendChild(clone.querySelector(<span class=\"hljs-string\">\":has(label)\"<\/span>));\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><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>In the script:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>A set of&nbsp;<code>genres<\/code>&nbsp;(tag values) is stored as an array<\/li>\n\n\n\n<li>For each item in the&nbsp;<code>genres<\/code>&nbsp;array, a new&nbsp;<code><a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Node\/cloneNode\">clone<\/a><\/code>&nbsp;is created from the&nbsp;<code><a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/HTMLTemplateElement\">template<\/a><\/code>&nbsp;that has an empty&nbsp;<code>plus<\/code>&nbsp;(checkbox) and&nbsp;<code>minus<\/code>&nbsp;(label) tag, as seen inside the&nbsp;<code>&lt;template&gt;<\/code>&nbsp;in HTML<\/li>\n\n\n\n<li>The empty tags\u2019 text and attributes are filled using the&nbsp;<code>genres<\/code>&nbsp;item\u2019s value and index<\/li>\n\n\n\n<li>Finally, the filled tags are added to their respective containers on the page \u2014&nbsp;<code>&lt;div&gt;<\/code>&nbsp;and&nbsp;<code>&lt;ul&gt;<\/code><\/li>\n<\/ol>\n\n\n\n<p>Here\u2019s how the HTML source code will look like once the page renders:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" 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\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"plus\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"checkbox\"<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"c1\"<\/span>&gt;<\/span>Adventure<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"plus\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"checkbox\"<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"c2\"<\/span>&gt;<\/span>Comedy<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"plus\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"checkbox\"<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"c3\"<\/span>&gt;<\/span>Thriller<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"plus\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"checkbox\"<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"c4\"<\/span>&gt;<\/span>Horror<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ul<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">li<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"minus\"<\/span> <span class=\"hljs-attr\">for<\/span>=<span class=\"hljs-string\">\"c1\"<\/span>&gt;<\/span>Adventure<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">li<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">li<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"minus\"<\/span> <span class=\"hljs-attr\">for<\/span>=<span class=\"hljs-string\">\"c2\"<\/span>&gt;<\/span>Comedy<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">li<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">li<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"minus\"<\/span> <span class=\"hljs-attr\">for<\/span>=<span class=\"hljs-string\">\"c3\"<\/span>&gt;<\/span>Thriller<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">li<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">li<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"minus\"<\/span> <span class=\"hljs-attr\">for<\/span>=<span class=\"hljs-string\">\"c4\"<\/span>&gt;<\/span>Horror<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">li<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\n&lt;\/<span class=\"hljs-attr\">ul<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><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<ol class=\"wp-block-list\">\n<li>The parent of each checkbox is&nbsp;<code>.plus<\/code>. They are meant to be the tags that are yet to be included. It\u2019s sectioned off inside a&nbsp;<code>&lt;div&gt;<\/code><\/li>\n\n\n\n<li>Each label is&nbsp;<code>.minus<\/code>&nbsp;\u2014 the tags that are included. These are arranged in a list inside a&nbsp;<code>&lt;ul&gt;<\/code><\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">CSS Tag Management<\/h2>\n\n\n\n<p>Let\u2019s look at the the key CSS rules first, then we\u2019ll simplify it. This provides the core functionality of the tag management.<\/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-comment\">\/* Remove all minus tags initially *\/<\/span>\n<span class=\"hljs-selector-tag\">li<\/span> { <span class=\"hljs-attribute\">display<\/span>: none; } \n\n<span class=\"hljs-comment\">\/* Remove a plus tag, if it's checked *\/<\/span>\n<span class=\"hljs-selector-class\">.plus<\/span><span class=\"hljs-selector-pseudo\">:has(input<\/span><span class=\"hljs-selector-pseudo\">:checked)<\/span>{\n    <span class=\"hljs-attribute\">display<\/span>: none; \n}\n\n<span class=\"hljs-comment\">\/* Remove a minus tag, if its plus tag is checked *\/<\/span>\n<span class=\"hljs-selector-pseudo\">:has(<\/span><span class=\"hljs-selector-id\">#c1<\/span><span class=\"hljs-selector-pseudo\">:checked)<\/span> <span class=\"hljs-selector-tag\">li<\/span><span class=\"hljs-selector-pseudo\">:has(<\/span><span class=\"hljs-selector-attr\">&#91;for=<span class=\"hljs-string\">'c1'<\/span>]<\/span>),\n<span class=\"hljs-selector-pseudo\">:has(<\/span><span class=\"hljs-selector-id\">#c2<\/span><span class=\"hljs-selector-pseudo\">:checked)<\/span> <span class=\"hljs-selector-tag\">li<\/span><span class=\"hljs-selector-pseudo\">:has(<\/span><span class=\"hljs-selector-attr\">&#91;for=<span class=\"hljs-string\">'c2'<\/span>]<\/span>),\n<span class=\"hljs-selector-pseudo\">:has(<\/span><span class=\"hljs-selector-id\">#c3<\/span><span class=\"hljs-selector-pseudo\">:checked)<\/span> <span class=\"hljs-selector-tag\">li<\/span><span class=\"hljs-selector-pseudo\">:has(<\/span><span class=\"hljs-selector-attr\">&#91;for=<span class=\"hljs-string\">'c3'<\/span>]<\/span>),\n<span class=\"hljs-selector-pseudo\">:has(<\/span><span class=\"hljs-selector-id\">#c4<\/span><span class=\"hljs-selector-pseudo\">:checked)<\/span> <span class=\"hljs-selector-tag\">li<\/span><span class=\"hljs-selector-pseudo\">:has(<\/span><span class=\"hljs-selector-attr\">&#91;for=<span class=\"hljs-string\">'c4'<\/span>]<\/span>) {\n    <span class=\"hljs-attribute\">display<\/span>: revert; \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<ol class=\"wp-block-list\">\n<li>Initially, the list items (<code>li<\/code>) with the .<code>minus<\/code>&nbsp;tags are not displayed. It means the user hasn\u2019t selected any tag to be included yet<\/li>\n\n\n\n<li>When user selects a tag \u2014 i.e. checks a checkbox (<code>:has(input:checked)<\/code>) \u2014 its parent element,&nbsp;<code>.plus<\/code>, is removed with&nbsp;<code>display:<\/code>&nbsp;&#8220;`none`<\/li>\n\n\n\n<li>And the corresponding&nbsp;<code>.minus<\/code>&nbsp;label\u2019s parent (ex.&nbsp;<code>li:has([for='c1']<\/code>) is made visible with&nbsp;<code>display: revert<\/code><\/li>\n<\/ol>\n\n\n\n<p class=\"learn-more\"><strong>Note<\/strong>: The CSS keyword&nbsp;<code>revert<\/code>&nbsp;changes a property value to its browser default<\/p>\n\n\n\n<p>To automate the selector listing at the end of the above CSS snippet, I\u2019ll add those rules in the script itself, so that it doesn\u2019t have to be hard-coded in CSS. Again, the whole point here is proving this can all be dynamically generated for your own set of tags if you wanted. If you don\u2019t prefer script, you can leave it as it is in CSS or use a CSS framework, whichever works for you. The following is a continuation of the previous JavaScript snippet, where only the code added now is shown.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> style = <span class=\"hljs-built_in\">document<\/span>.createElement(<span class=\"hljs-string\">'style'<\/span>);\n<span class=\"hljs-built_in\">document<\/span>.head.appendChild(style);\n\n<span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">let<\/span> i = <span class=\"hljs-number\">0<\/span>; i &lt; genres.length; i++) {\n  <span class=\"hljs-comment\">\/* ... *\/<\/span>\n  style.sheet.insertRule(<span class=\"hljs-string\">`:has(#c<span class=\"hljs-subst\">${i+<span class=\"hljs-number\">1<\/span>}<\/span>:checked) li:has(&#91;for='c<span class=\"hljs-subst\">${i+<span class=\"hljs-number\">1<\/span>}<\/span>']) { display: revert; }`<\/span>);\n}\n\n<span class=\"hljs-comment\">\/* a clear view of the CSS rule string from the above snippet *\/<\/span>\n<span class=\"hljs-string\">`:has(#c<span class=\"hljs-subst\">${i+<span class=\"hljs-number\">1<\/span>}<\/span>:checked) li:has(&#91;for='c<span class=\"hljs-subst\">${i+<span class=\"hljs-number\">1<\/span>}<\/span>']) { display: revert; }`<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><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>In the above script: A new&nbsp;<code>style<\/code>&nbsp;is added to the page, and to this&nbsp;<code>style<\/code>&nbsp;a css rule for each&nbsp;<code>genres<\/code>&nbsp;items is added. The css rule is same as in the css snippet from before. Here the&nbsp;<code>id<\/code>&nbsp;values are dynamically generated.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">CSS Tag Styling<\/h2>\n\n\n\n<p>Let\u2019s style the tags\u2019 appearance:<\/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-selector-class\">.plus<\/span>,\n<span class=\"hljs-selector-class\">.minus<\/span> {\n  <span class=\"hljs-attribute\">display<\/span>: inline-block;\n  <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-number\">1<\/span>lh;\n  <span class=\"hljs-attribute\">padding-inline-end<\/span>: <span class=\"hljs-number\">1.5em<\/span>;\n  <span class=\"hljs-attribute\">border-radius<\/span>: <span class=\"hljs-number\">4px<\/span>;\n  <span class=\"hljs-attribute\">border<\/span>: <span class=\"hljs-number\">1px<\/span> solid currentColor;\n  <span class=\"hljs-attribute\">text-indent<\/span>: <span class=\"hljs-number\">10%<\/span>;\n  <span class=\"hljs-attribute\">font-weight<\/span>: bold;\n  &amp;::after {\n    <span class=\"hljs-attribute\">display<\/span>: block;\n    <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">100%<\/span>;\n    <span class=\"hljs-attribute\">margin-block-start<\/span>: -<span class=\"hljs-number\">1<\/span>lh;\n    <span class=\"hljs-attribute\">margin-inline-start<\/span>: <span class=\"hljs-number\">1.2em<\/span>;\n    <span class=\"hljs-attribute\">text-align<\/span>: right;\n  }\n  &amp;<span class=\"hljs-selector-pseudo\">:hover<\/span> {\n    <span class=\"hljs-attribute\">box-shadow<\/span>: <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">10px<\/span> white, <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">6px<\/span> currentColor;\n  }\n}\n<span class=\"hljs-selector-class\">.plus<\/span> {\n  <span class=\"hljs-attribute\">position<\/span>: relative;\n  <span class=\"hljs-attribute\">color<\/span>: <span class=\"hljs-built_in\">rgb<\/span>(<span class=\"hljs-number\">118<\/span>, <span class=\"hljs-number\">201<\/span>, <span class=\"hljs-number\">140<\/span>);\n  <span class=\"hljs-attribute\">margin-inline<\/span>: <span class=\"hljs-number\">0.5em<\/span>;\n  &amp;::after {\n    <span class=\"hljs-attribute\">content<\/span>: <span class=\"hljs-string\">\"+\"<\/span>;\n  }\n}\n<span class=\"hljs-selector-class\">.minus<\/span> {\n  <span class=\"hljs-attribute\">color<\/span>: <span class=\"hljs-built_in\">rgb<\/span>(<span class=\"hljs-number\">95<\/span>, <span class=\"hljs-number\">163<\/span>, <span class=\"hljs-number\">228<\/span>);\n  &amp;::after {\n    <span class=\"hljs-attribute\">content<\/span>: <span class=\"hljs-string\">\"\\02212\"<\/span>;\n  }\n}\n<span class=\"hljs-selector-tag\">input<\/span><span class=\"hljs-selector-attr\">&#91;type=<span class=\"hljs-string\">\"checkbox\"<\/span>]<\/span> {\n  <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">100%<\/span>;\n  <span class=\"hljs-attribute\">height<\/span>: inherit;\n  <span class=\"hljs-attribute\">border-radius<\/span>: inherit;\n  <span class=\"hljs-attribute\">appearance<\/span>: none;\n  <span class=\"hljs-attribute\">position<\/span>: absolute;\n  <span class=\"hljs-attribute\">left<\/span>: <span class=\"hljs-number\">0<\/span>;\n  <span class=\"hljs-attribute\">top<\/span>: <span class=\"hljs-number\">0<\/span>;\n  <span class=\"hljs-attribute\">margin<\/span>: <span class=\"hljs-number\">0<\/span>;\n}\n<span class=\"hljs-selector-tag\">input<\/span><span class=\"hljs-selector-attr\">&#91;type=<span class=\"hljs-string\">\"checkbox\"<\/span>]<\/span>,\n<span class=\"hljs-selector-tag\">label<\/span> {\n  <span class=\"hljs-attribute\">cursor<\/span>: pointer;\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>\n\n\n<ol class=\"wp-block-list\">\n<li>The&nbsp;<code>.plus<\/code>&nbsp;and&nbsp;<code>.minus<\/code>&nbsp;tags are colored and bordered. Each has a pseudo-element (<code>::after<\/code>) to add the \u201c+\u201d and \u201c-\u201c icon next to it, respectively<\/li>\n\n\n\n<li>The checkbox\u2019s default appearance is removed and is made to fill the area of its container element, so the entire container is clickable<\/li>\n<\/ol>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_VwNQxvZ\" src=\"\/\/codepen.io\/anon\/embed\/VwNQxvZ?height=450&amp;theme-id=47434&amp;slug-hash=VwNQxvZ&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed VwNQxvZ\" title=\"CodePen Embed VwNQxvZ\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">CSS Dynamic Notification<\/h2>\n\n\n\n<p>Notification messages can be displayed to the users in certain circumstances, and this can also be done entirely in CSS:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>When&nbsp;<em>no<\/em>&nbsp;tags are selected<\/li>\n\n\n\n<li>When&nbsp;<em>at least one<\/em>&nbsp;tag is selected<\/li>\n\n\n\n<li>When&nbsp;<em>all<\/em>&nbsp;tags have been selected<\/li>\n<\/ol>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">ul<\/span> {\n  &amp;::before {\n    <span class=\"hljs-attribute\">display<\/span>: block;\n    <span class=\"hljs-attribute\">text-align<\/span>: center;\n    <span class=\"hljs-attribute\">margin-block-start<\/span>: <span class=\"hljs-number\">1<\/span>lh;\n  }\n  <span class=\"hljs-selector-pseudo\">:not(<\/span><span class=\"hljs-selector-pseudo\">:has(input<\/span><span class=\"hljs-selector-pseudo\">:checked))<\/span> &amp;<span class=\"hljs-selector-pseudo\">::before<\/span> {\n    <span class=\"hljs-comment\">\/* has no checked boxes *\/<\/span>\n    <span class=\"hljs-attribute\">content<\/span>: <span class=\"hljs-string\">\"No tags included yet\"<\/span>;\n  }\n  <span class=\"hljs-selector-pseudo\">:has(input<\/span><span class=\"hljs-selector-pseudo\">:checked)<\/span><span class=\"hljs-selector-pseudo\">:has(input<\/span><span class=\"hljs-selector-pseudo\">:not(<\/span><span class=\"hljs-selector-pseudo\">:checked))<\/span> &amp;<span class=\"hljs-selector-pseudo\">::before<\/span> {\n    <span class=\"hljs-comment\">\/* has atleast one checked and unchecked boxes *\/<\/span>\n    <span class=\"hljs-attribute\">content<\/span>: <span class=\"hljs-string\">\"Following tags are included\"<\/span>;\n  }\n  <span class=\"hljs-selector-pseudo\">:not(<\/span><span class=\"hljs-selector-pseudo\">:has(input<\/span><span class=\"hljs-selector-pseudo\">:not(<\/span><span class=\"hljs-selector-pseudo\">:checked)))<\/span> &amp;<span class=\"hljs-selector-pseudo\">::before<\/span> {\n    <span class=\"hljs-comment\">\/* has no unchecked boxes *\/<\/span>\n    <span class=\"hljs-attribute\">content<\/span>: <span class=\"hljs-string\">\"All tags are included\"<\/span>;\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><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>In the above CSS&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/CSS_nesting\">nested<\/a>&nbsp;code snippet:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><code>&amp;<\/code>&nbsp;represents&nbsp;<code>&lt;ul&gt;<\/code>. Hence&nbsp;<code>&amp;::before<\/code>&nbsp;means&nbsp;<code>ul:before<\/code><\/li>\n\n\n\n<li><code>:has()<\/code>&nbsp;and&nbsp;<code>:not(:has())<\/code>&nbsp;represent when the root element (the page) contains a given selector (mentioned inside the parentheses) and when it doesn\u2019t<\/li>\n\n\n\n<li><code>input:checked<\/code>&nbsp;is a checked box<\/li>\n\n\n\n<li><code>input:not(:checked)<\/code>&nbsp;is an unchecked box<\/li>\n<\/ol>\n\n\n\n<p>And here\u2019s what\u2019s happening in the code:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>A&nbsp;<code>::before<\/code>&nbsp;pseudo-element is added to the&nbsp;<code>&lt;ul&gt;<\/code>. This serves as the notification area<\/li>\n\n\n\n<li>When the page has no checked element,&nbsp;<code>'No tags included yet'<\/code>&nbsp;is displayed<\/li>\n\n\n\n<li>When the page has at least one checked and one unchecked box,&nbsp;<code>'Following tags are included'<\/code>&nbsp;is shown<\/li>\n\n\n\n<li>When the page doesn\u2019t have any unchecked box,&nbsp;<code>'All tags are included'<\/code>&nbsp;appears<\/li>\n<\/ol>\n\n\n\n<p class=\"learn-more\"><strong>Tip<\/strong>: Instead of the root element you can scope the code to a common parent, too. Ex. using&nbsp;<code>main:has()<\/code>&nbsp;instead of&nbsp;<code>:has()<\/code>, when&nbsp;<code>&lt;main&gt;<\/code>&nbsp;is a common ancestor of the two group of tags<\/p>\n\n\n\n<p>Here\u2019s the final demo:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_WNWXvve\" src=\"\/\/codepen.io\/anon\/embed\/WNWXvve?height=450&amp;theme-id=47434&amp;slug-hash=WNWXvve&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed WNWXvve\" title=\"CodePen Embed WNWXvve\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Because there\u2019s so much independence between the two set of tags, it frees you up in styling the tags however you like. You can embed the tags in sentences if you want, or you could have as many elements in between them as you want without worrying. For as long as the user is well informed by the design the purpose of the tags, and which ones have been selected, and which ones remain unselected, design them however you feel like.<\/p>\n\n\n\n<p class=\"learn-more\"><strong>Bonus<\/strong>: You can even use CSS counters to add a running count of selected and unselected tags, if you want. Read on CSS counters for a similar use case&nbsp;<a href=\"https:\/\/css-tricks.com\/counting-css-counters-css-grid\/\">here<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Checkboxes and labels&nbsp;used&nbsp;to have to be right next to each other to be a potent UI duo. You could do trickery like this: But now, thanks to&nbsp;:has()&nbsp;in CSS, we\u2019re not beholden to that structure anymore.&nbsp;We can :has() it all, as it is said. Now that these HTML elements have some autonomy, without losing their connection [&hellip;]<\/p>\n","protected":false},"author":20,"featured_media":1656,"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":[40,7,151],"class_list":["post-1650","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-has","tag-css","tag-template"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/tags-thumb.jpg?fit=1000%2C500&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/1650","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\/20"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=1650"}],"version-history":[{"count":10,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/1650\/revisions"}],"predecessor-version":[{"id":1671,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/1650\/revisions\/1671"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/1656"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=1650"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=1650"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=1650"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}