{"id":3002,"date":"2024-07-12T12:41:35","date_gmt":"2024-07-12T18:41:35","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=3002"},"modified":"2024-07-12T12:41:36","modified_gmt":"2024-07-12T18:41:36","slug":"css-does-need-mixins","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/css-does-need-mixins\/","title":{"rendered":"Style Queries are Almost Like Mixins (But Mixins Would Be Better)"},"content":{"rendered":"\n<p>I was styling a menu thing the other day, and it had some decently nested selectors. Normally I&#8217;m a pretty big fan of putting a class right on the thing you want to style and keeping CSS selectors pretty &#8220;flat&#8221; in that they are just that class alone. But for menus and the semantic HTML within, a little nesting seemed reasonable. For instance this HTML:<\/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\">nav<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"site-nav\"<\/span>&gt;<\/span>\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\">a<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"#\"<\/span>&gt;<\/span>Home<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/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\">a<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"#\"<\/span>&gt;<\/span>Contact<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/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\">a<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"#\"<\/span>&gt;<\/span>About<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/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\">a<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"#\"<\/span>&gt;<\/span>History<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/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\">ul<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">nav<\/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>Can lead to this kind of CSS:<\/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\">.site-nav<\/span> {\n  &gt; ul {\n    &gt; li {\n      &gt; a {\n        &amp;:hover, \n        &amp;:focus {\n        }\n      }\n    }\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>I can see how that turns some people off, but honestly it doesn&#8217;t bother me that much. The structure is reliable here and I&#8217;d rather this setup in CSS than a class on every one of those links in the HTML. <\/p>\n\n\n\n<p>If we get into <em>sub-<\/em>menu territory though, it gets gnarlier:<\/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\">.site-nav<\/span> {\n  &gt; ul {\n    &gt; li {\n      &gt; ul { <span class=\"hljs-comment\">\/* sub menu *\/<\/span>\n        &gt; li {\n          &gt; a {\n            &amp;:hover,\n            &amp;:focus {\n              \n            }\n          }\n        }\n      }\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>I&#8217;m willing to admit this is probably a bit too far in nesting town \ud83d\ude2c. Particularly because at each of those nested levels there will be a bunch of styling and it will become hard to reason about quite quickly.<\/p>\n\n\n\n<p>It occurred to me that the (newfangled, not production-ready) CSS style queries might be able to jump in  and help here, because they behave a bit like a mixin. <\/p>\n\n\n\n<p>Mixin?<\/p>\n\n\n\n<p>Yeah! That&#8217;s what Sass called the concept, anyway. A mixin allows us to <em>name<\/em> a block of styles, then call them as needed. So&#8230;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"SCSS\" data-shcb-language-slug=\"scss\"><span><code class=\"hljs language-scss\"><span class=\"hljs-keyword\">@mixin<\/span> linkHovered {\n  <span class=\"hljs-attribute\">background<\/span>: red;\n  <span class=\"hljs-attribute\">color<\/span>: white; \n}\n\n<span class=\"hljs-selector-class\">.site-nav<\/span> {\n  &gt; <span class=\"hljs-selector-tag\">ul<\/span> {\n    &gt; <span class=\"hljs-selector-tag\">li<\/span> {\n      &gt; <span class=\"hljs-selector-tag\">a<\/span> {\n        &amp;<span class=\"hljs-selector-pseudo\">:hover<\/span>, \n        &amp;<span class=\"hljs-selector-pseudo\">:focus<\/span> {\n          <span class=\"hljs-keyword\">@include<\/span> linkHovered;\n        }\n      }\n    }\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\">SCSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">scss<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>So now we&#8217;ve <em>kind of<\/em> flattened out the styles a bit. We have this re-usable chunk of styles that we can just call rather than nest the styles so deeply. <\/p>\n\n\n\n<p>What I wanted to try here was using Style Queries (and <em>not<\/em> Sass), but unfortunately it&#8217;s not quite as clean as I&#8217;d like. After using Sass for so long, this is what I wanted to work:<\/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-comment\">\/* Invalid! Doesn't select anything *\/<\/span>\n<span class=\"hljs-keyword\">@container<\/span> style(--linkHovered) {\n  <span class=\"hljs-selector-tag\">background<\/span>: <span class=\"hljs-selector-tag\">red<\/span>;\n}\n\n<span class=\"hljs-selector-class\">.site-nav<\/span> {\n  &gt; ul {\n    &gt; li {\n      &gt; a {\n        &amp;:hover, &amp;:focus {\n          <span class=\"hljs-attribute\">--linkHovered<\/span>: true;\n        }\n      }\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>But that&#8217;s a no-go. The <code>@container<\/code> style query either needs to be nested within the other styles so that it has an implied selector (which defeats the &#8220;flatten the styles&#8221; purpose) or it needs an explicit selector inside it. So it needs to be 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\"><span class=\"hljs-keyword\">@container<\/span> style(--hasLinkHovered) {\n  <span class=\"hljs-selector-tag\">a<\/span> {\n    <span class=\"hljs-attribute\">background<\/span>: red;\n  }\n}\n\n<span class=\"hljs-selector-class\">.site-nav<\/span> {\n  &gt; ul {\n    &gt; li {\n      &amp;:has(&gt; <span class=\"hljs-attribute\">a<\/span>:hover, &gt; a:focus) {\n        --hasLinkHovered: true;\n      }\n    }\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\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>That works (<a href=\"https:\/\/caniuse.com\/css-container-queries-style\">where supported<\/a>): <\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_gONpgVe\" src=\"\/\/codepen.io\/anon\/embed\/gONpgVe?height=450&amp;theme-id=47434&amp;slug-hash=gONpgVe&amp;default-tab=css,result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed gONpgVe\" title=\"CodePen Embed gONpgVe\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>I just don&#8217;t love it. You have to set the Custom Property higher up in the nesting because container styles can&#8217;t style the thing they query. Plus now the true selector is a combination of the nesting and what&#8217;s in the container style query which is an awful brainbuster to keep track of. <\/p>\n\n\n\n<p>This is <em>not<\/em> to say that Style Queries aren&#8217;t useful. They totally are, and I&#8217;m sure we&#8217;ll uncover lots of cool use cases in the coming years. I&#8217;m just saying that shoehorning them to behave exactly like mixins isn&#8217;t great.<\/p>\n\n\n\n<p>It sure would be nice if <a href=\"https:\/\/css.oddbird.net\/sasslike\/mixins-functions\/\">CSS got native mixins<\/a>! <\/p>\n\n\n\n<p>It would be yet another Sass feature making it&#8217;s way into the platform. In the case of mixins, it would be a great win, because the CSS would be more efficient than the way Sass had to express the mixin concept back in CSS. If you used a <code>@mixin<\/code> 10 times under different selectors, those styles blocks would be barfed out 10 duplicate times in the CSS. Perhaps not the worlds biggest deal thanks to file compression, but certainly not as efficient as the language itself just referring to a single block of styles. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Having a named block of styles to apply in CSS can be useful, and newfangled Style Queries are pretty close to that. We look at one use case here, how Sass did mixins better, and hope for a native solution. <\/p>\n","protected":false},"author":1,"featured_media":3004,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[7,206,205],"class_list":["post-3002","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-css","tag-mixins","tag-style-queries"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/07\/czNmcy1wcml2YXRlL3Jhd3BpeGVsX2ltYWdlcy93ZWJzaXRlX2NvbnRlbnQvbHIvaXM5NTE3LWltYWdlLWt3dnlmaGQ2LmpwZw.webp?fit=1024%2C683&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/3002","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=3002"}],"version-history":[{"count":4,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/3002\/revisions"}],"predecessor-version":[{"id":3007,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/3002\/revisions\/3007"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/3004"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=3002"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=3002"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=3002"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}