{"id":239,"date":"2023-12-11T22:21:34","date_gmt":"2023-12-11T22:21:34","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=239"},"modified":"2023-12-15T20:44:52","modified_gmt":"2023-12-15T20:44:52","slug":"quantity-queries-are-very-easy-with-css-has","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/quantity-queries-are-very-easy-with-css-has\/","title":{"rendered":"Quantity Queries are Very Easy with CSS :has()"},"content":{"rendered":"\n<p>What is a quantity query? It&#8217;s a bit of CSS that allows you to style an element (and its descendants) based on <em>how many children<\/em> the element has. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Example of a Quantity Query Situation<\/h2>\n\n\n\n<p>Imagine a homepage currently showing 20 articles, more than you normally show. Maybe it&#8217;s a slow news day, and the lead editor is shooting for variety. So because that&#8217;s a lot, you want CSS to scale them down a bit or re-arrange them to make them more equally browsable. <\/p>\n\n\n\n<p>But then you have a really big news day with a lead story, so you decide only to show five articles, the first of which you want to make very large. <\/p>\n\n\n\n<p>Quantity queries (originally <a href=\"https:\/\/alistapart.com\/article\/quantity-queries-for-css\/\">coined by Heydon Pickering<\/a>, best I can tell) may be a solution for this. Here&#8217;s some pseudo code to explain:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">main<\/span> {\n  \n  <span class=\"hljs-comment\">\/* if (articles &gt; 20) *\/<\/span>\n  <span class=\"hljs-attribute\">display<\/span>: grid;\n  <span class=\"hljs-attribute\">grid-template-columns<\/span>: <span class=\"hljs-built_in\">repeat<\/span>(<span class=\"hljs-number\">5<\/span>, <span class=\"hljs-number\">1<\/span>fr);\n\n  <span class=\"hljs-comment\">\/* if (articles &gt; 5) *\/<\/span>\n  <span class=\"hljs-attribute\">display<\/span>: flex;\n  <span class=\"hljs-attribute\">flex-wrap<\/span>: flex;\n  <span class=\"hljs-attribute\">article<\/span>:first-of-type { width: <span class=\"hljs-number\">100%<\/span> }\n\n  <span class=\"hljs-comment\">\/* default *\/<\/span>\n  <span class=\"hljs-selector-tag\">display<\/span>: <span class=\"hljs-selector-tag\">block<\/span>;\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<h2 class=\"wp-block-heading\">Old School Quantity Query<\/h2>\n\n\n\n<p>Believe it or not, this has been possible in CSS for a while! It just involves some trickery. Here&#8217;s Heydon&#8217;s first example:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">button<\/span> {\n  <span class=\"hljs-attribute\">font-size<\/span>: <span class=\"hljs-number\">2em<\/span>;\n}\n\n<span class=\"hljs-selector-tag\">button<\/span><span class=\"hljs-selector-pseudo\">:not(<\/span><span class=\"hljs-selector-pseudo\">:only-of-type)<\/span> {\n  <span class=\"hljs-attribute\">font-size<\/span>: <span class=\"hljs-number\">1.25em<\/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>See what that&#8217;s doing? That second selector is saying: if this button isn&#8217;t <em>totally alone<\/em> in its parent element, scale down the <code>font-size<\/code>. Essentially: if <em>one<\/em>, do this, if <em>more<\/em>, do this. A simple quantity query. With increasingly complicated selectors like that, you can pull of quantity math. You didn&#8217;t see it very often though, as it was pretty weird and complicated. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Quantity Queries with :has()<\/h2>\n\n\n\n<p>Now we have <code>:has()<\/code> in CSS, supported across all the major browsers as of just this month, and we can use it to make quantity queries a lot easier. <\/p>\n\n\n\n<p>Here&#8217;s how it works.<\/p>\n\n\n\n<p>You check <em>if<\/em> an element has an element at all at an <code>:nth-child()<\/code> position. For example, to check if an element has <strong>10 or more<\/strong> elements (a quantity query), you can do:<\/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-class\">.element<\/span> {\n  &amp;:has(&gt; :nth-child(10)) {\n\n    <span class=\"hljs-comment\">\/* Style things knowing that \n       `.element` has at least 10 \n       children *\/<\/span>\n\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>You can keep going if you like. Just know that for each of them that matches, all the styles will be applied, so it&#8217;s rather additive. <\/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-class\">.element<\/span> {\n\n  &amp;:has(&gt; :nth-child(10)) { }\n  &amp;<span class=\"hljs-selector-pseudo\">:has(<\/span>&gt; <span class=\"hljs-selector-pseudo\">:nth-child(20))<\/span> { }\n  &amp;<span class=\"hljs-selector-pseudo\">:has(<\/span>&gt; <span class=\"hljs-selector-pseudo\">:nth-child(30))<\/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>(Note the <code>&gt;<\/code> child selector is <a href=\"https:\/\/twitter.com\/ChallengesCss\/status\/1735364976238284979\">a bit safer<\/a> than not using it, as it protects against some descendant element having this many children, which is probably not what you mean.)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>You could also make sure you&#8217;re checking for a particular element type. Like perhaps doing some special styling for a menu that has 10 options within it (or more), knowing that <a href=\"https:\/\/developer.chrome.com\/blog\/hr-in-select#:~:text=The%20select%20element%20gets%20a,for%20a%20better%20user%20experience.\">select menus can contain <code>&lt;hr \/&gt;<\/code> element seperators now<\/a>, which aren&#8217;t actually menu options. <\/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\">selectlist<\/span> {\n\n  &amp;:has(<span class=\"hljs-attribute\">option<\/span>:<span class=\"hljs-built_in\">nth-of-type<\/span>(<span class=\"hljs-number\">10<\/span>)) { }\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>(Wondering what a <code>&lt;selectlist&gt;<\/code> is? We can&#8217;t use it quite yet, but it&#8217;s <a href=\"https:\/\/open-ui.org\/components\/selectlist\/#design-decisions\">looking to be<\/a> a 100% CSS styleable <code>&lt;select&gt;<\/code>, which is awesome). <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Example<\/h2>\n\n\n\n<p>Here&#8217;s a demo where a range input changes the number of children elements are within a parent, then changes the styling of the children depending on how many there are.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_vYbPaxo\" src=\"\/\/codepen.io\/anon\/embed\/vYbPaxo?height=450&amp;theme-id=47434&amp;slug-hash=vYbPaxo&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed vYbPaxo\" title=\"CodePen Embed vYbPaxo\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>What other things can you think of where quantity queries would benefit? Perhaps you would style an article differently if it contains 5 or more header elements. Perhaps you could style table rows to look &#8220;collapsed&#8221; if there are over a certain threshold of them. Perhaps you would style a comment thread differently if a certain comment has more than 10 replies. <\/p>\n\n\n\n<div class=\"wp-block-group learn-more\"><div class=\"wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained\">\n<p>Wanna keep digging into more? Jen Kramer&#8217;s course <a href=\"https:\/\/frontendmasters.com\/courses\/intermediate-html-css\/?utm_source=boost&amp;utm_medium=blog&amp;utm_campaign=boost\">Intermediate HTML &amp; CSS<\/a> gets into this in the video <a href=\"https:\/\/frontendmasters.com\/courses\/intermediate-html-css\/level-4-pseudo-class-selectors\/?utm_source=boost&amp;utm_medium=blog&amp;utm_campaign=boost\">Level 4 Pseudo-Class Selectors<\/a>.<\/p>\n<\/div><\/div>\n","protected":false},"excerpt":{"rendered":"<p>What is a quantity query? It&#8217;s a bit of CSS that allows you to style an element (and its descendants) based on how many children the element has. Example of a Quantity Query Situation Imagine a homepage currently showing 20 articles, more than you normally show. Maybe it&#8217;s a slow news day, and the lead [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":245,"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,41],"class_list":["post-239","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-has","tag-css","tag-quantity-queries"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2023\/12\/queries-thumb.jpg?fit=1000%2C500&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/239","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=239"}],"version-history":[{"count":10,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/239\/revisions"}],"predecessor-version":[{"id":272,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/239\/revisions\/272"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/245"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=239"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=239"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=239"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}