{"id":126,"date":"2023-12-06T16:30:46","date_gmt":"2023-12-06T16:30:46","guid":{"rendered":"http:\/\/fem.flywheelsites.com\/?p=126"},"modified":"2024-02-20T21:35:21","modified_gmt":"2024-02-21T03:35:21","slug":"fine-ill-use-a-super-basic-css-processing-setup","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/fine-ill-use-a-super-basic-css-processing-setup\/","title":{"rendered":"Fine, I&#8217;ll Use a Super Basic CSS Processing Setup."},"content":{"rendered":"\n<p><strong>If you&#8217;d like, you can <a href=\"#lets-wire-up-a-modern-css-processing-setup\">jump straight to the setup<\/a>, but I&#8217;d like to lay down my thinking first. Here goes.  <\/strong><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>These days I <em>try<\/em> to use just CSS. You don&#8217;t need a CSS processor these days like you used to, thanks to native CSS getting <em>so much<\/em> better over the years. <\/p>\n\n\n\n<p>I used to reach for <a href=\"https:\/\/sass-lang.com\/\">Sass<\/a> like seconds on Thanksgiving. <em>Of course<\/em> I&#8217;m going for it. There was so much value there! Nesting, loops, mixins, includes, variables, math, just a cornucopia of value. Most of that stuff is in CSS now, and <em>better.<\/em><\/p>\n\n\n\n<p>Except includes. <em>Dammit<\/em>.<\/p>\n\n\n\n<p>Good ones, anyway. Native CSS has <code>@import<\/code> which is technically an include. I suppose it predates ESM imports by gotta be somewhere near 2 decades. But all it does is trigger another HTTP Request for another stylesheet. It doesn&#8217;t concatenate them together which is generally what you want with CSS. <em>Bundle<\/em>, as it were.<\/p>\n\n\n\n<p>So Sass made <code>@import<\/code> do bundling. That was a weird call, actually, since it trampled over a native CSS feature in a way that Sass was usually very good about <em>not<\/em> doing. <\/p>\n\n\n\n<p>And I&#8217;d usually lump on <a href=\"https:\/\/postcss.org\/\">PostCSS<\/a> too, mostly because <a href=\"https:\/\/github.com\/postcss\/autoprefixer\">Autoprefixer<\/a> had a real hay day of usefulness. These days, there is only precious few things left that even need it anymore (looking at you, <code>mask<\/code>). <\/p>\n\n\n\n<p>So, between Sass and PostCSS (and probably a specialized minifier), that was getting to be a rather complex CSS processing pipeline. Plus, you&#8217;d always have to figure out how to wire it up with whatever other site-building dance you were doing. Maybe you had your own favorites. I know there are Less and Stylus people out there, not to mention a soup of potential PostCSS plugins, specialty tools like CSS modules, and the rest of the iceberg of CSS-in-JS machines. <\/p>\n\n\n\n<p>That fatigued me, and so again: these days I <em>try<\/em> to use just CSS.<\/p>\n\n\n\n<p>But there are still growing pains as a single CSS file gets bigger. The length of the file can get annoying. The odd CSS property or value that still needs prefixing. The desire to use some new CSS feature that would be easier with a sprinkle of processing.<\/p>\n\n\n\n<p>SO FINE:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"the-setup\">Let&#8217;s wire up a modern CSS processing setup.<\/h2>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"alignright size-full is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"400\" height=\"400\" src=\"https:\/\/i0.wp.com\/rc.frontendmasters.com\/blog\/wp-content\/uploads\/2023\/11\/lightningcss-logo.jpg?resize=400%2C400\" alt=\"\" class=\"wp-image-141\" style=\"aspect-ratio:1;width:172px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2023\/11\/lightningcss-logo.jpg?w=400&amp;ssl=1 400w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2023\/11\/lightningcss-logo.jpg?resize=300%2C300&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2023\/11\/lightningcss-logo.jpg?resize=150%2C150&amp;ssl=1 150w\" sizes=\"auto, (max-width: 400px) 100vw, 400px\" \/><\/figure>\n<\/div>\n\n\n<p>We&#8217;re going to use <a href=\"https:\/\/lightningcss.dev\/\">Lightning CSS<\/a>. It has one job: processing CSS. But offers a compelling set of features:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Bundling<\/li>\n\n\n\n<li>Syntax Lowering<\/li>\n\n\n\n<li>Vendor prefixing<\/li>\n\n\n\n<li>Minification<\/li>\n<\/ol>\n\n\n\n<p>I&#8217;ll get to the details of those as we go. <\/p>\n\n\n\n<p>First, let&#8217;s get it installed. Here&#8217;s both the core and the CLI because that&#8217;s where we want to use it:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">npm install lightningcss lightningcss-cli --save-dev<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Lightning CSS is from the Parcel project, but here we&#8217;re not using it via Parcel, we&#8217;re using it directly. It&#8217;s now ready to be called at the command line.<\/p>\n\n\n\n<p>Say we have a <code>style.css<\/code> file and we&#8217;re going to process it into a <code>style-min.css<\/code> file:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">lightningcss --bundle --targets <span class=\"hljs-string\">'&gt;= 0.25%'<\/span> --minifystyle.css -o style-min.css<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Sweet, done. <\/p>\n\n\n\n<p>Notice we&#8217;re specifically asking for the features we want via flags.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">We&#8217;re going to want a file watcher.<\/h3>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/assets.codepen.io\/3\/lighting-working.gif?ssl=1\" alt=\"\"\/><\/figure>\n\n\n\n<p>We could make this script easier to run by putting it in our <code>package.json<\/code> like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\">  <span class=\"hljs-string\">\"scripts\"<\/span>: {\n    <span class=\"hljs-attr\">\"process\"<\/span>: <span class=\"hljs-string\">\"lightningcss --bundle --targets '&gt;= 0.25%' --minifystyle.css -o style-min.css\"<\/span>\n  },<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>But that&#8217;s no fun. <\/p>\n\n\n\n<p>Now every time we write some CSS we have to come back to the command line, run <code>npm run process<\/code> and then head over to the browser and refresh it to see the changes? We&#8217;ve made the choice opt-in to some extra technology here, so let&#8217;s get the DX right. <\/p>\n\n\n\n<p>Let&#8217;s use <a href=\"https:\/\/github.com\/gajus\/turbowatch\">Turbowatch<\/a> to:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Watch our CSS file(s)<\/li>\n\n\n\n<li>Run the command to process the CSS when the files change<\/li>\n<\/ol>\n\n\n\n<p>I&#8217;m no Turbowatch expert but I found it straightforward to set up.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">npm install turbowatch --save-dev<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Let&#8217;s make our npm script run Turbowatch instead:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\">  <span class=\"hljs-string\">\"scripts\"<\/span>: {\n    <span class=\"hljs-attr\">\"dev\"<\/span>: <span class=\"hljs-string\">\"turbowatch .\/turbowatch.ts\"<\/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\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>So we&#8217;ll need that <code>turbowatch.ts<\/code> file. Fortunately, it&#8217;s straightforward:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"TypeScript\" data-shcb-language-slug=\"typescript\"><span><code class=\"hljs language-typescript\"><span class=\"hljs-keyword\">import<\/span> { defineConfig } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"turbowatch\"<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> defineConfig({\n  project: __dirname,\n  triggers: &#91;\n    {\n      expression: &#91;<span class=\"hljs-string\">\"match\"<\/span>, <span class=\"hljs-string\">\"*.css\"<\/span>, <span class=\"hljs-string\">\"basename\"<\/span>],\n      name: <span class=\"hljs-string\">\"build\"<\/span>,\n      onChange: <span class=\"hljs-keyword\">async<\/span> ({ spawn }) =&gt; {\n        <span class=\"hljs-keyword\">await<\/span> spawn<span class=\"hljs-string\">`lightningcss --minify --bundle style.css -o style-min.css`<\/span>;\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\">TypeScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">typescript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Match files that end in <code>*.css<\/code>, when they change, run the command. <\/p>\n\n\n\n<p>Cool.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Let&#8217;s add a live reloader.<\/h2>\n\n\n\n<p>Fair warning, this is extremely old school and definitely not the only option, but I like how easy it is: <a href=\"https:\/\/livejs.com\/\">Live.js.<\/a> <\/p>\n\n\n\n<p>It&#8217;s a script you link up and it monitors the resources on the page and refreshes when they change. So now&#8230;<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>We update our CSS file<\/li>\n\n\n\n<li>Turbowatcher sees the changes<\/li>\n\n\n\n<li>Lightning CSS runs and makes a new CSS file<\/li>\n\n\n\n<li>Live.js sees the change and refreshes the page<\/li>\n<\/ol>\n\n\n\n<p>That&#8217;s the complete DX we&#8217;re after here. <\/p>\n\n\n\n<p>Case closed. Lightweight CSS build process complete.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">About those Lighting CSS features<\/h2>\n\n\n\n<h4 class=\"wp-block-heading\">Bundling<\/h4>\n\n\n\n<p>Like Sass, it&#8217;s slightly weird that the concatenation\/bundling feature uses <code>@import<\/code>, which is the native CSS syntax. I sort of don&#8217;t blame them as otherwise Lightning CSS doesn&#8217;t invent any CSS syntax, which is a pretty special line to cross, and would probably mean they&#8217;d have to stop using <code>.css<\/code> and use <code>.lightning<\/code> instead or something. <\/p>\n\n\n\n<p>I say that, but the <a href=\"https:\/\/lightningcss.dev\/bundling.html#conditional-imports\">conditional syntax<\/a> for imports actually <em>is<\/em> an invented syntax. Strange. Probably would have went with <code>@bundle<\/code> or something then, so that the native <code>@import<\/code> behavior would be preserved. As it is, if you try to use the bundling feature and, say try to use <a href=\"https:\/\/open-props.style\/\">Open Props<\/a> via <code>@import \"https:\/\/unpkg.com\/open-props\";<\/code> or the like, it will error. <\/p>\n\n\n\n<p>I still think bundling is a crucial feature here and probably wouldn&#8217;t use Lightning CSS at all if it didn&#8217;t have it.<\/p>\n\n\n\n<p>I can do:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">\/project\/\n  \/css\/\n    footer.css\n    header.css\n    layout.css\n    reset.css\n  style.css<\/pre>\n\n\n\n<p>And <code>style.css<\/code> can bundle those all together during processing as long as they are referenced:<\/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\">@import<\/span> <span class=\"hljs-string\">\"footer.css\"<\/span>;\n<span class=\"hljs-keyword\">@import<\/span> <span class=\"hljs-string\">\"header.css\"<\/span>;\n<span class=\"hljs-keyword\">@import<\/span> <span class=\"hljs-string\">\"layout.css\"<\/span>;\n<span class=\"hljs-keyword\">@import<\/span> <span class=\"hljs-string\">\"reset.css\"<\/span>;<\/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>The order is preserved.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Syntax Lowering<\/h4>\n\n\n\n<p>Lightning CSS makes the choices on what CSS to process down to code that older browsers understand based on what browsers you actually want to support, ala <a href=\"https:\/\/browserslist.dev\/?q=bGFzdCAyIHZlcnNpb25z\">Browserslist<\/a>.<\/p>\n\n\n\n<p>Say I want to take advantage of the OKLCH color model (and I do!). I could write&#8230;<\/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-selector-tag\">html<\/span> {\n  <span class=\"hljs-attribute\">--color-dark-purple<\/span>: <span class=\"hljs-built_in\">oklch<\/span>(<span class=\"hljs-number\">15.96%<\/span> <span class=\"hljs-number\">0.079<\/span> <span class=\"hljs-number\">316<\/span>);\n  <span class=\"hljs-attribute\">--color-bright-purple<\/span>: <span class=\"hljs-built_in\">oklch<\/span>(<span class=\"hljs-number\">45.08%<\/span> <span class=\"hljs-number\">0.24<\/span> <span class=\"hljs-number\">308<\/span>);\n  <span class=\"hljs-attribute\">--color-light-purple<\/span>: <span class=\"hljs-number\">#dabbe0<\/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\">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>And, as I write, with Browserslist set to <code>defaults<\/code>, the output will be:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">html<\/span> {\n  <span class=\"hljs-attribute\">--color-dark-purple<\/span>: <span class=\"hljs-number\">#190023<\/span>;\n  <span class=\"hljs-attribute\">--color-bright-purple<\/span>: <span class=\"hljs-number\">#7900b5<\/span>;\n  <span class=\"hljs-attribute\">--color-light-purple<\/span>: <span class=\"hljs-number\">#dabbe0<\/span>;\n}\n\n<span class=\"hljs-keyword\">@supports<\/span> (<span class=\"hljs-attribute\">color:<\/span> lab(<span class=\"hljs-number\">0%<\/span> <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">0<\/span>)) {\n  <span class=\"hljs-selector-tag\">html<\/span> {\n    <span class=\"hljs-attribute\">--color-dark-purple<\/span>: <span class=\"hljs-built_in\">lab<\/span>(<span class=\"hljs-number\">2.88618%<\/span> <span class=\"hljs-number\">14.7006<\/span> -<span class=\"hljs-number\">16.436<\/span>);\n    <span class=\"hljs-attribute\">--color-bright-purple<\/span>: <span class=\"hljs-built_in\">lab<\/span>(<span class=\"hljs-number\">31.5834%<\/span> <span class=\"hljs-number\">65.0634<\/span> -<span class=\"hljs-number\">67.2974<\/span>);\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><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>Notice the HEX color was left alone, and the OKLCH values were set into <code>lab()<\/code> instead. I&#8217;m not even 100% sure why, but it must be that I&#8217;ll get the best cross-browser support of that. That&#8217;s nice to not have to think intensely about.<\/p>\n\n\n\n<p>It&#8217;s not just colors, it&#8217;s lots of features of &#8220;modern CSS&#8221;. Take, for instance, CSS nesting. I can now write:<\/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-tag\">div<\/span> {\n  div {\n    <span class=\"hljs-attribute\">border-block-start<\/span>: <span class=\"hljs-number\">1px<\/span> solid red;\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>We&#8217;re close, but not all browsers are supporting native nesting yet. And some browsers right now will choke on that nested selector not &#8220;starting with a symbol&#8221;. So Lightning CSS will process that to:<\/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\"><span class=\"hljs-selector-tag\">div<\/span> <span class=\"hljs-selector-tag\">div<\/span> {\n  <span class=\"hljs-attribute\">border-block-start<\/span>: <span class=\"hljs-number\">1px<\/span> solid red;\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>That&#8217;s supported everywhere, so fine with me! But notice it left the logical property alone. The browsers you get from the <code>default<\/code> setting all support that, so it&#8217;s left alone.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Vendor Prefixing<\/h4>\n\n\n\n<p>There isn&#8217;t terribly much to be said here, but it does what it says on the box. <\/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\"><span class=\"hljs-selector-class\">.mask<\/span> {\n  <span class=\"hljs-attribute\">mask<\/span>: <span class=\"hljs-built_in\">url<\/span>(mask.png);\n}<\/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>Turns into:<\/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-selector-class\">.mask<\/span> {\n<\/span><\/span><mark class='shcb-loc'><span>  <span class=\"hljs-attribute\">-webkit-mask<\/span>: <span class=\"hljs-built_in\">url<\/span>(<span class=\"hljs-string\">\"mask.png\"<\/span>);\n<\/span><\/mark><span class='shcb-loc'><span>  <span class=\"hljs-attribute\">mask<\/span>: <span class=\"hljs-built_in\">url<\/span>(<span class=\"hljs-string\">\"mask.png\"<\/span>);\n<\/span><\/span><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<h4 class=\"wp-block-heading\">Minification<\/h4>\n\n\n\n<p>This is a great feature of Lightning CSS, because it saves us from needing a separate tool for this. Plus, they say it&#8217;s the best tool on the market for squeezing the size of that CSS down, so <em>bonus<\/em>. Sass always had a &#8220;compressed&#8221; option for output, but nobody trusted it to <em>truly<\/em> minify CSS. Lightning CSS will do the job nicely. <\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"978\" height=\"344\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2023\/12\/comparison.png?resize=978%2C344&#038;ssl=1\" alt=\"\" class=\"wp-image-253\" style=\"aspect-ratio:2.8430232558139537;width:420px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2023\/12\/comparison.png?w=978&amp;ssl=1 978w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2023\/12\/comparison.png?resize=300%2C106&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2023\/12\/comparison.png?resize=768%2C270&amp;ssl=1 768w\" sizes=\"auto, (max-width: 978px) 100vw, 978px\" \/><\/figure>\n<\/div>\n\n\n<h4 class=\"wp-block-heading\">CSS Modules<\/h4>\n\n\n\n<p>I figured I&#8217;d point out that Lightning CSS can do CSS Modules. I&#8217;m a fan of CSS Modules, the way it does scoped styles is very useful, but generally only in the context of React where I&#8217;m in a position to <code>import<\/code> the styles and apply the proper <code>className<\/code>. For a very simple CSS processing like this, which doesn&#8217;t involve the HTML or templates in any way, we don&#8217;t need CSS modules.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Example Project<\/h2>\n\n\n\n<p>Wanna see all this functional on a project so you can copy the bits you need like we all do every day? Heck yes, you do:<\/p>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/github.com\/chriscoyier\/simple-lightning-css-setup\">Example Project on GitHub<\/a><\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>If you need a mini build process just for your CSS, Lightning CSS is a pretty good modern choice. That, plus a file watcher and live reloader offers pretty good DX.<\/p>\n","protected":false},"author":1,"featured_media":134,"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":[34,7,33],"class_list":["post-126","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-build-process","tag-css","tag-lightning-css"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2023\/11\/lightning-featured.png?fit=1000%2C500&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/126","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=126"}],"version-history":[{"count":16,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/126\/revisions"}],"predecessor-version":[{"id":986,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/126\/revisions\/986"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/134"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=126"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=126"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=126"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}