{"id":8020,"date":"2025-12-11T17:29:27","date_gmt":"2025-12-11T22:29:27","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=8020"},"modified":"2025-12-11T17:37:01","modified_gmt":"2025-12-11T22:37:01","slug":"thoughts-on-native-css-mixins","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/thoughts-on-native-css-mixins\/","title":{"rendered":"Thoughts on Native CSS Mixins"},"content":{"rendered":"\n<p>I have some notes from various times I&#8217;ve thought about the idea of native CSS mixins so I figured I&#8217;d get &#8217;em down on (digital) paper! <\/p>\n\n\n\n<p>For the record, they don&#8217;t really exist yet, but Miriam Suzanne <a href=\"https:\/\/www.oddbird.net\/2024\/06\/11\/removing-mixins\/\">says<\/a>:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>The&nbsp;CSS&nbsp;Working Group has agreed to move forward with&nbsp;CSS-native mixins.<\/p>\n<\/blockquote>\n\n\n\n<p>And there is a <a href=\"https:\/\/www.w3.org\/TR\/css-mixins-1\/\">spec<\/a>, but the spec only deals with <code>@function<\/code> (which <em>does<\/em> <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/Reference\/At-rules\/@function\">exist<\/a>). Functions are a little similar but act only as a single value rather than a block of styles.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The idea comes from Sass <code>@mixin<\/code>.<\/h2>\n\n\n\n<p>We happen to use Sass (SCSS) at CodePen and as I write, we have 328 <a href=\"https:\/\/sass-lang.com\/documentation\/at-rules\/mixin\/\"><code>@mixin<\/code> definitions<\/a> in the codebase, so it&#8217;s clearly of use.<\/p>\n\n\n\n<p>Here&#8217;s a practical-if-basic example:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"SCSS\" data-shcb-language-slug=\"scss\"><span><code class=\"hljs language-scss shcb-code-table\"><mark class='shcb-loc'><span><span class=\"hljs-keyword\">@mixin<\/span> cover {\n<\/span><\/mark><span class='shcb-loc'><span>  <span class=\"hljs-attribute\">position<\/span>: absolute;\n<\/span><\/span><span class='shcb-loc'><span>  inset: <span class=\"hljs-number\">0<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><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>In Sass, that doesn&#8217;t compile to anything. You have to use it. Like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"SCSS\" data-shcb-language-slug=\"scss\"><span><code class=\"hljs language-scss shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-selector-class\">.modal-overlay<\/span> {\n<\/span><\/span><mark class='shcb-loc'><span>  <span class=\"hljs-keyword\">@include<\/span> cover;\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-selector-class\">.card<\/span><span class=\"hljs-selector-class\">.disabled<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  &amp;<span class=\"hljs-selector-pseudo\">::before<\/span> {\n<\/span><\/span><mark class='shcb-loc'><span>    <span class=\"hljs-keyword\">@include<\/span> cover;\n<\/span><\/mark><span class='shcb-loc'><span>    <span class=\"hljs-attribute\">background<\/span>: lch(<span class=\"hljs-number\">0%<\/span> <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">0<\/span> \/ <span class=\"hljs-number\">0.8<\/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-2\"><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>See how I&#8217;ve used it <em>twice<\/em> above. Compiled Sass will dump in the contents of the mixin in both places:<\/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 shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-selector-class\">.modal-overlay<\/span> {\n<\/span><\/span><mark class='shcb-loc'><span>  <span class=\"hljs-attribute\">position<\/span>: absolute;\n<\/span><\/mark><mark class='shcb-loc'><span>  <span class=\"hljs-attribute\">inset<\/span>: <span class=\"hljs-number\">0<\/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-selector-class\">.card<\/span><span class=\"hljs-selector-class\">.disabled<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  &amp;::before {\n<\/span><\/span><mark class='shcb-loc'><span>    <span class=\"hljs-attribute\">position<\/span>: absolute;\n<\/span><\/mark><mark class='shcb-loc'><span>    <span class=\"hljs-attribute\">inset<\/span>: <span class=\"hljs-number\">0<\/span>;\n<\/span><\/mark><span class='shcb-loc'><span>    <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-built_in\">lch<\/span>(<span class=\"hljs-number\">0%<\/span> <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">0<\/span> \/ <span class=\"hljs-number\">0.8<\/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-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>Things can get a little fancier in Sass, but it&#8217;s all pretty straightforward:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Mixins can include nesting and work in nested code. They can even slot in nested content you pass to it.<\/li>\n\n\n\n<li>Mixins can use other mixins. <\/li>\n\n\n\n<li>Mixins can have parameters (like a function) and use\/calculate off those values in the output.<\/li>\n<\/ul>\n\n\n\n<p>I would assume and hope that all of this is supported in native CSS mixins. The native version, as explained so far on Miriam&#8217;s site (which will almost definitley change!), the only difference is the usage syntax:<\/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 shcb-code-table\"><mark class='shcb-loc'><span><span class=\"hljs-keyword\">@mixin<\/span> --cover {\n<\/span><\/mark><span class='shcb-loc'><span>  <span class=\"hljs-selector-tag\">position<\/span>: <span class=\"hljs-selector-tag\">absolute<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-selector-tag\">inset<\/span>: 0;\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-selector-class\">.modal-overlay<\/span> {\n<\/span><\/span><mark class='shcb-loc'><span>  @apply --cover;\n<\/span><\/mark><span class='shcb-loc'><span>}\n<\/span><\/span><\/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>I imagine it&#8217;s <code>@apply<\/code> instead of <code>@include<\/code> literally because Sass uses <code>@include<\/code> and Sass would have a hard time &#8220;leaving it alone&#8221; when processing down to CSS.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Is there enough here for browsers\/standards to actually do it?<\/h2>\n\n\n\n<p>The W3C CSS Working Group has already OK&#8217;d the idea of all this, so I assume it&#8217;s already been determined there is value to native CSS having this ability at all. But what are those reasons? <\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Not having to reach for a preprocessor tool like Sass.<\/strong> I don&#8217;t think this is enough of a reason all by itself for them, but personally, I do. This is a paved cowpath, as they say.<\/li>\n\n\n\n<li><strong>Preprocessor output has potentially a lot of duplicate code.<\/strong> This leads to bigger CSS files. Perhaps not a huge issue with gzip\/brotli in play, but still, smaller files is almost always good. <\/li>\n\n\n\n<li><strong>Integration with <code>--custom-properties<\/code>.<\/strong> I would think the parameters could be custom properties and there could be custom properties used generally with the style block. Custom properties can change dynamically, causing re-evaluated styles, so mixins can become more powerful expressions of style based on a comparatively small custom property change. <\/li>\n\n\n\n<li><strong>Custom Properties can cascade<\/strong> and be different values at different points in the DOM, so mixins might also do different things at different points in the DOM. All this custom property stuff would be impossible in a preprocessor.<\/li>\n\n\n\n<li><strong>It&#8217;s a nicer API than faking it with <code>@container style()<\/code><\/strong>. You can test a custom property with a style query and dump out styles in certain places now, but it doesn&#8217;t feel quite right.<\/li>\n<\/ul>\n\n\n\n<p>I wonder what else tipped the scales toward the working group doing it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Parameter handling seems tricky.<\/h2>\n\n\n\n<p>You can pass things to a mixin, which I think is pretty crucial to their value.<\/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-keyword\">@mixin<\/span> --setColors(--color) {\n  <span class=\"hljs-selector-tag\">color<\/span>: <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--color<\/span>);\n  <span class=\"hljs-selector-tag\">background-color<\/span>: <span class=\"hljs-selector-tag\">oklch<\/span>(<span class=\"hljs-selector-tag\">from<\/span> <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--color<\/span>) <span class=\"hljs-selector-tag\">calc<\/span>(<span class=\"hljs-selector-tag\">l<\/span> <span class=\"hljs-selector-tag\">-<\/span> 40%) <span class=\"hljs-selector-tag\">c<\/span> <span class=\"hljs-selector-tag\">h<\/span> \/ 0<span class=\"hljs-selector-class\">.9<\/span>);\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 things can get weird with params. Like what happens if you call <code>setColors()<\/code> with no params? Does it just fail and output nothing? <\/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-selector-class\">.card<\/span> {\n  @apply --setColors(); <span class=\"hljs-comment\">\/* ??? *\/<\/span>\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>It&#8217;s possible <code>--color<\/code> is set anyway at the cascade level it&#8217;s being used at, so maybe it has access to that and outputs anyway? I assume if <code>--color<\/code> is set at the same cascade level <em>and<\/em> the param is passed, the param value wins? How does <code>!important<\/code> factor in?<\/p>\n\n\n\n<p>And what about typed params? And default values? Seems doable but quite verbose feeling, especially for CSS. Is it like&#8230;<\/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\"><span class=\"hljs-keyword\">@mixin<\/span> --setColors(\n  --color type(color): red\n) {\n  <span class=\"hljs-selector-tag\">color<\/span>: <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--color<\/span>);\n  <span class=\"hljs-selector-tag\">background-color<\/span>: <span class=\"hljs-selector-tag\">oklch<\/span>(<span class=\"hljs-selector-tag\">from<\/span> <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--color<\/span>) <span class=\"hljs-selector-tag\">calc<\/span>(<span class=\"hljs-selector-tag\">l<\/span> <span class=\"hljs-selector-tag\">-<\/span> 40%) <span class=\"hljs-selector-tag\">c<\/span> <span class=\"hljs-selector-tag\">h<\/span> \/ 0<span class=\"hljs-selector-class\">.9<\/span>);\n}<\/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>Maybe like that? I&#8217;m not sure what the syntax limitations are. Or maybe we don&#8217;t need default values at all because the <code>var()<\/code> syntax supports fallbacks already?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Feels like it could open up a world of more third-party CSS usage. <\/h2>\n\n\n\n<p>Imagine <a href=\"https:\/\/chrome.dev\/carousel-configurator\/\">CSS carousels<\/a>. They are so cool. And they are quite a bit of CSS code. Perhaps their usage could be abstracted into a <code>@mixin<\/code>. <\/p>\n\n\n\n<p>The jQuery days were something like this pseudo-code:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ &lt;script src=\"\/plugins\/owl-carousel.js\"&gt;&lt;\/script&gt;<\/span>\n$(<span class=\"hljs-string\">\".owl-carousel\"<\/span>).owlCarousel({\n  <span class=\"hljs-attr\">gap<\/span>: <span class=\"hljs-number\">10<\/span>, \n  <span class=\"hljs-attr\">navArrows<\/span>: <span class=\"hljs-literal\">true<\/span>,\n  <span class=\"hljs-attr\">navDots<\/span>: <span class=\"hljs-literal\">true<\/span>\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><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>Which morphed into JavaScript components:<\/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\">import<\/span> SlickCarousel <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"slickcarousel\"<\/span>;\n\n<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">SlickCarousel<\/span>\n  <span class=\"hljs-attr\">gap<\/span>=<span class=\"hljs-string\">\"10\"<\/span>\n  <span class=\"hljs-attr\">navArrows<\/span>=<span class=\"hljs-string\">{true}<\/span>\n  <span class=\"hljs-attr\">navDots<\/span>=<span class=\"hljs-string\">{true}<\/span>\n\/&gt;<\/span><\/span><\/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>Maybe that becomes:<\/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\">@import<\/span> <span class=\"hljs-string\">\"\/node_modules\/radcarousel\/carousel.css\"<\/span>;\n\n<span class=\"hljs-selector-class\">.carousel<\/span> {\n  @apply --radCarousel(\n    <span class=\"hljs-attribute\">--gap<\/span>: <span class=\"hljs-number\">10px<\/span>,\n    --navArrows: true,\n    --navDots: true\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>\n\n\n<p>The jQuery version was DIY HTML and this would be too. You could call that SSR for free, kids.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What about &#8220;private&#8221; variables? <\/h2>\n\n\n\n<p>I sort of remember Miriam talking about this at CSS Day this past year. I think this was the issue:<\/p>\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 shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-keyword\">@mixin<\/span> --my-thing {\n<\/span><\/span><mark class='shcb-loc'><span>  <span class=\"hljs-selector-tag\">--space<\/span>: 1<span class=\"hljs-selector-tag\">rem<\/span>;\n<\/span><\/mark><span class='shcb-loc'><span>  <span class=\"hljs-selector-tag\">gap<\/span>: <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--space<\/span>);\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-selector-tag\">margin<\/span>: <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--space<\/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-selector-class\">.card<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  @apply --my-thing;\n<\/span><\/span><mark class='shcb-loc'><span>  <span class=\"hljs-attribute\">padding<\/span>: <span class=\"hljs-built_in\">var<\/span>(--space); <span class=\"hljs-comment\">\/* defined or not? *\/<\/span>\n<\/span><\/mark><span class='shcb-loc'><span>}\n<\/span><\/span><\/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>The question is, does that <code>--space<\/code> custom property &#8220;leak out&#8221; when you apply the mixin and thus can be used there? It either 1) does 2) doesn&#8217;t 3) some explicit syntax is needed. <\/p>\n\n\n\n<p>I can imagine it being useful to &#8220;leak&#8221; (return) them, so say you wanted that behavior by default, but the option to not do that. Maybe it needs to be like&#8230;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" 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-keyword\">@mixin<\/span> --my-thing {\n<\/span><\/span><mark class='shcb-loc'><span>  <span class=\"hljs-keyword\">@private<\/span> {\n<\/span><\/mark><mark class='shcb-loc'><span>    <span class=\"hljs-selector-tag\">--space<\/span>: 1<span class=\"hljs-selector-tag\">rem<\/span>;\n<\/span><\/mark><mark class='shcb-loc'><span>  }\n<\/span><\/mark><span class='shcb-loc'><span>  <span class=\"hljs-selector-tag\">gap<\/span>: <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--space<\/span>);\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-selector-tag\">margin<\/span>: <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--space<\/span>);\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><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>Don&#8217;t hate it. Miriams post also mentions being more explicit about what is returned like using an <code>@output<\/code> block or privatizing custom properties with a <code>!private<\/code> flag.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What about source order? <\/h2>\n\n\n\n<p>What happens here?<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" 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-keyword\">@mixin<\/span> --set-vars {\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-selector-tag\">--papaBear<\/span>: 30<span class=\"hljs-selector-tag\">px<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-selector-tag\">--mamaBear<\/span>: 20<span class=\"hljs-selector-tag\">px<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-selector-tag\">--babyBear<\/span>: 10<span class=\"hljs-selector-tag\">px<\/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-selector-class\">.card<\/span> {\n<\/span><\/span><mark class='shcb-loc'><span>  <span class=\"hljs-attribute\">--papaBear<\/span>: <span class=\"hljs-number\">50px<\/span>;\n<\/span><\/mark><mark class='shcb-loc'><span>  @apply --set-vars;\n<\/span><\/mark><mark class='shcb-loc'><span>  <span class=\"hljs-attribute\">margin<\/span>: <span class=\"hljs-built_in\">var<\/span>(--papaBear);\n<\/span><\/mark><span class='shcb-loc'><span>}\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><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>What <code>margin<\/code> would get set here? 50px because it&#8217;s set right there? 30px because it&#8217;s being overridden by the mixin? <em>What if you reversed the order of the first two lines?<\/em> Will source order be the determining factor here?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Are Custom Idents required?<\/h2>\n\n\n\n<p>All the examples use the <code>--my-mixin<\/code> style naming, with the double-dashes in front, like custom properties have. This type of using is called a &#8220;custom ident&#8221; as far as I understand it. It&#8217;s what custom functions are required to use, and they <a href=\"https:\/\/www.w3.org\/TR\/css-mixins-1\/\">share the same spec<\/a>, so I would think it would be required for mixins too. <\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-comment\">\/* \ud83d\udeab *\/<\/span>\n<span class=\"hljs-keyword\">@mixin<\/span> doWork {\n}\n\n<span class=\"hljs-comment\">\/* \u2705 *\/<\/span>\n<span class=\"hljs-keyword\">@mixin<\/span> --doWork {\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><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>Is this just like the way forward for all custom named things forever in CSS? I think it&#8217;s required for anchor names too, but not container names? I wish it was consistent, but I like backwards compatibility better so I can live.<\/p>\n\n\n\n<p>Wouldn&#8217;t it be better if it was required for keyframes, for example? Like if you saw this code below, is it obvious what the user-named word is and what other things are language syntax features?<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.leaving<\/span> {\n  <span class=\"hljs-attribute\">animation<\/span>: slide <span class=\"hljs-number\">0.2s<\/span> forwards;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><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>It&#8217;s <code>slide<\/code> here, so you&#8217;d have to go find it:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@keyframes<\/span> slide {\n  <span class=\"hljs-selector-tag\">to<\/span> { <span class=\"hljs-attribute\">translate<\/span>: -<span class=\"hljs-number\">200px<\/span> <span class=\"hljs-number\">0<\/span>; }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><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>To me it would be much more clear if it was:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.leaving<\/span> {\n  <span class=\"hljs-attribute\">animation<\/span>: --slide <span class=\"hljs-number\">0.2s<\/span> forwards;\n}\n<span class=\"hljs-keyword\">@keyframes<\/span> --slide {\n  <span class=\"hljs-selector-tag\">to<\/span> { <span class=\"hljs-attribute\">translate<\/span>: -<span class=\"hljs-number\">200px<\/span> <span class=\"hljs-number\">0<\/span>; }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><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>Annnnnnnd there is nothing really stopping us from doing that so maybe we should. Or take it one step further and adopt <a href=\"https:\/\/nerdy.dev\/css-emoji-convention\">an emoji naming structure<\/a>. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Calling Multiple Mixins<\/h2>\n\n\n\n<p>Would it be like?<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@apply<\/span> --mixin-one, --mixin-two;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><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>Maybe space-separated?<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@apply<\/span> --mixin-one --mixin-two;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><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>Or that is weird? Maybe you just gotta do it individually?<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@apply<\/span> --mixin-one;\n<span class=\"hljs-keyword\">@apply<\/span> --mixin-two;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><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>Does it matter?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Functions + Mixins<\/h2>\n\n\n\n<p>It seems to make sense that a mixin could call a function&#8230;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-21\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@mixin<\/span> --box {\n  <span class=\"hljs-selector-tag\">gap<\/span>: <span class=\"hljs-selector-tag\">--get-spacing<\/span>(2);\n  <span class=\"hljs-selector-tag\">margin-trim<\/span>: <span class=\"hljs-selector-tag\">block<\/span>;\n  &gt; * {\n    <span class=\"hljs-attribute\">padding<\/span>: <span class=\"hljs-built_in\">--get-spacing<\/span>(<span class=\"hljs-number\">4<\/span>);\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-21\"><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 would it be forbidden the other way around, a function calling a mixin?<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-22\" 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-keyword\">@function<\/span> --get-spacing(--size) {\n<\/span><\/span><mark class='shcb-loc'><span>  <span class=\"hljs-keyword\">@apply<\/span> get-vars(); <span class=\"hljs-comment\">\/* ??? *\/<\/span>\n<\/span><\/mark><span class='shcb-loc'><span>  <span class=\"hljs-selector-tag\">result<\/span>: \n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-selector-tag\">if<\/span> (\n<\/span><\/span><mark class='shcb-loc'><span>      <span class=\"hljs-selector-tag\">style<\/span>(<span class=\"hljs-selector-tag\">--some-other-var<\/span>: <span class=\"hljs-selector-tag\">xxx<\/span>): 3<span class=\"hljs-selector-tag\">rem<\/span>;\n<\/span><\/mark><span class='shcb-loc'><span>      <span class=\"hljs-selector-tag\">style<\/span>(<span class=\"hljs-selector-tag\">--size<\/span>: 2): 1<span class=\"hljs-selector-tag\">rem<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-selector-tag\">style<\/span>(<span class=\"hljs-selector-tag\">--size<\/span>: 4): 2<span class=\"hljs-selector-tag\">rem<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-selector-tag\">else<\/span>: 0<span class=\"hljs-selector-class\">.5rem<\/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-22\"><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>Or is that fine?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Infinite Loops<\/h2>\n\n\n\n<p>Is it possible this opens up infinite loop problems in calculated styles? I don&#8217;t know if this is an actual problem but it&#8217;s brain-bending to me.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-23\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@mixin<\/span> --foo(--val) {\n  <span class=\"hljs-selector-tag\">--val<\/span>: 2;\n}\n\n<span class=\"hljs-selector-class\">.parent<\/span> {\n  <span class=\"hljs-attribute\">--val<\/span>: <span class=\"hljs-number\">1<\/span>;\n  .thing {\n    @apply --foo(--val);\n    <span class=\"hljs-attribute\">--val<\/span>: <span class=\"hljs-built_in\">if<\/span>(\n        style(--val: <span class=\"hljs-number\">1<\/span>): <span class=\"hljs-number\">2<\/span>;\n        <span class=\"hljs-attribute\">else<\/span>: <span class=\"hljs-number\">1<\/span>;\n      );\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-23\"><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>Like, when evaluating a <code>.thing<\/code>, <code>--val<\/code> is 1 because of inheritance, but then we apply a mixin which changes it to 2, then we reset it back to 1, but if it&#8217;s 1 shouldn&#8217;t it reevaluate to 2? I just don&#8217;t know. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Unmixing<\/h2>\n\n\n\n<p>Miriam asks <a href=\"https:\/\/www.oddbird.net\/2024\/06\/11\/removing-mixins\/\">can you un-mix a mixin?<\/a> Which is a great question. It&#8217;s very worth thinking about, because if there ends up being an elegant way to do it, it makes native mixins even more powerful and a big feather in their cap above what any preprocessor can do. I don&#8217;t hate an <code>@unapply<\/code> at first thought. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Thoughts?<\/h2>\n\n\n\n<p>Are you stoked for native mixins? Against it? Worried?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>There are no browser implementations of mixins yet, nor a fleshed out spec. So perhaps now is the best time to try to understand and opine.<\/p>\n","protected":false},"author":1,"featured_media":8035,"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],"class_list":["post-8020","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-css","tag-mixins"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/12\/mixin-thumb-1.jpg?fit=2000%2C1200&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/8020","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=8020"}],"version-history":[{"count":12,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/8020\/revisions"}],"predecessor-version":[{"id":8034,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/8020\/revisions\/8034"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/8035"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=8020"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=8020"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=8020"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}