<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Bart Veneman</title>
    <description>The latest articles on Forem by Bart Veneman (@bartveneman).</description>
    <link>https://forem.com/bartveneman</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F973835%2F80aadfcf-9d1c-478d-9f7d-c4f8483a5bbe.png</url>
      <title>Forem: Bart Veneman</title>
      <link>https://forem.com/bartveneman</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/bartveneman"/>
    <language>en</language>
    <item>
      <title>CSS @imports are awesome</title>
      <dc:creator>Bart Veneman</dc:creator>
      <pubDate>Fri, 22 Sep 2023 07:16:47 +0000</pubDate>
      <link>https://forem.com/bartveneman/css-imports-are-awesome-1fn9</link>
      <guid>https://forem.com/bartveneman/css-imports-are-awesome-1fn9</guid>
      <description>&lt;p&gt;One of the most inspirational things lately is to watch at &lt;a href="https://nerdy.dev/"&gt;Adam Argyle's&lt;/a&gt; side projects and see how he's doing some really nerdy CSS work. It was &lt;a href="https://codepen.io/argyleink/pen/PoxQrNj"&gt;somewhere in his work&lt;/a&gt; where I found it, in all it's glory. Waiting to be explored, a rich journey ahead, anxious for the CSS developer community's love and approval. It's everyone's favorite CSS at-rule: &lt;code&gt;@import&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;Triggered by my feature requests for project Wallace to extract media queries and supports rules from imports, I started reading into the humble rule. And then Romain &lt;a href="https://github.com/romainmenke/css-import-tests"&gt;started a repository&lt;/a&gt; with a bunch of browser and bundler tests to verify different levels of support. In this day of the componentized web and scoped styling solutions it's grown out of favour. For years we've been told to &lt;a href="https://csswizardry.com/2017/02/code-smells-in-css-revisited/#css-import"&gt;avoid &lt;code&gt;@import&lt;/code&gt;&lt;/a&gt; for performance reasons. But if you take a closer look, you'll find that &lt;code&gt;@import&lt;/code&gt; is &lt;strong&gt;packed&lt;/strong&gt; with a ton of features that actually make you want to use this bad boy.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Flexible URL syntax&lt;/li&gt;
&lt;li&gt;Cascade Layers&lt;/li&gt;
&lt;li&gt;Conditional imports: &lt;code&gt;supports()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Conditional imports: media queries&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Flexible syntax: &lt;code&gt;"url"&lt;/code&gt;, &lt;code&gt;url('')&lt;/code&gt;, or &lt;code&gt;url()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The most important thing an &lt;code&gt;@import&lt;/code&gt; needs to do is to import CSS rules some location. The location part here is quite important to the at-rule, so luckily it's very easy to write the URL correctly. Right?&lt;/p&gt;

&lt;p&gt;Well. Let's have a look at these examples:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;'https://example.com/style.css'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;"https://example.com/style.css"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="sx"&gt;url(https://example.com/style.css)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="sx"&gt;url('https://example.com/style.css')&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="sx"&gt;url("https://example.com/style.css")&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yup, they're all the same thing. Even the one wrapped within &lt;code&gt;url()&lt;/code&gt; with no quotes at all! Apparently there are &lt;a href="https://drafts.csswg.org/css-values-4/#urls"&gt;legacy reasons&lt;/a&gt; to allow that. The spec also says this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Some CSS contexts (such as &lt;code&gt;@import&lt;/code&gt;) also allow a &lt;code&gt;&amp;lt;url&amp;gt;&lt;/code&gt; to be represented by a bare &lt;code&gt;&amp;lt;string&amp;gt;&lt;/code&gt;, without the function wrapper. In such cases the string behaves identically to a &lt;code&gt;url()&lt;/code&gt; function containing that string.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's good to know that there's at least 5 different ways to specify the URL for &lt;code&gt;@import&lt;/code&gt;. Look at you being all flexible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cascade Layers
&lt;/h2&gt;

&lt;p&gt;Next up: one of the best additions to CSS in recent years: Cascade Layers! This has me all excited because Bramus gave a thrilling talk at CSS Day last year about it's workings and capabilities. And then I saw Adam's CodePen profile packed with example usage of &lt;code&gt;layer()&lt;/code&gt; in &lt;code&gt;@import&lt;/code&gt;. Here's three from the Pen I linked in the intro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;'https://unpkg.com/open-props'&lt;/span&gt; &lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;design&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;'https://unpkg.com/open-props/normalize.min.css'&lt;/span&gt; &lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;demo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;support&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;'https://unpkg.com/open-props/buttons.min.css'&lt;/span&gt; &lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;demo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;support&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because &lt;code&gt;@import&lt;/code&gt; needs to be defined at the top of the document it can be troublesome to let the CSS end up in the correct layer, but the import sytax filled that gap by allowing you to specify which layer you want to put the imported rules in. If you know how &lt;code&gt;@layer&lt;/code&gt; works, you can probably tell that his layering system looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@layer&lt;/span&gt; &lt;span class="n"&gt;design&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;/* ... */&lt;/span&gt;

    &lt;span class="k"&gt;@layer&lt;/span&gt; &lt;span class="n"&gt;system&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;/* ... */&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@layer&lt;/span&gt; &lt;span class="n"&gt;demo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;/* ... */&lt;/span&gt;

    &lt;span class="k"&gt;@layer&lt;/span&gt; &lt;span class="n"&gt;support&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;/* ... */&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These were all named layers, but you can also import into an anonymous layer. It is allowed to specify layers before imports, so you could also name you layers first and then specify your imports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@layer&lt;/span&gt; &lt;span class="n"&gt;design&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;demo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;support&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;'https://unpkg.com/open-props'&lt;/span&gt; &lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;design&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;'https://cookie-consent-stinks.com/bad-css-1.css'&lt;/span&gt; &lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;'https://marketing-junk.com/bad-css-2.css'&lt;/span&gt; &lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that last import's rules will be assigned to a new, anonymous layer. The benefit here is that the bad CSS in each of these imports will be contained withing their own layers and don't leak out to the other layers. This might save you a bunch of headaches down the road.&lt;/p&gt;

&lt;h3&gt;
  
  
  Browser support
&lt;/h3&gt;

&lt;p&gt;It seems like &lt;a href="https://caniuse.com/mdn-css_at-rules_import_supports"&gt;&lt;code&gt;layer&lt;/code&gt; browser support in imports&lt;/a&gt; has been around for for over a year already. They probably picked this up right alongside developing initial &lt;code&gt;@layer&lt;/code&gt; efforts.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; element &lt;a href="https://twitter.com/bramus/status/1593376033725591552"&gt;does not (yet) support&lt;/a&gt; importing into a layer, so if you really must add some 3rd party CSS into a layer, this is your bet. For now. Keep an eye on &lt;a href="https://github.com/whatwg/html/issues/7540"&gt;this GitHub thread&lt;/a&gt; if you want to know if and when support is coming.&lt;/p&gt;

&lt;h2&gt;
  
  
  Import conditions
&lt;/h2&gt;

&lt;p&gt;Just like how &lt;code&gt;@import&lt;/code&gt; cannot be nested inside &lt;code&gt;@layer&lt;/code&gt;, you also can't conditionally load CSS by writing &lt;code&gt;@import&lt;/code&gt; inside an &lt;code&gt;@supports&lt;/code&gt; or &lt;code&gt;@media&lt;/code&gt;. But you &lt;strong&gt;can&lt;/strong&gt; conditionally load CSS by appending your supports or media query to the end of the import rule.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;supports()&lt;/code&gt; in &lt;code&gt;@import&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;You can append a &lt;code&gt;supports()&lt;/code&gt; supports-condition to an import to only import the specific CSS in case the supports-condition is met. The spec has a pretty cool example where they load a fallback stylesheet in case the browser does not support flexbox.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="sx"&gt;url('fallback-layout.css')&lt;/span&gt; &lt;span class="n"&gt;supports&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;@supports&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;/* ... */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could think of this import as this (although this is not valid and will not work):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Mental picture only, this does not work and is not valid CSS */&lt;/span&gt;
&lt;span class="k"&gt;@supports&lt;/span&gt; &lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="sx"&gt;url('fallback-layout.css')&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@import#importing_css_rules_conditional_on_feature_support"&gt;MDN goes a step beyond&lt;/a&gt; that and loads this CSS only if &lt;code&gt;display: flex&lt;/code&gt; is supported and &lt;code&gt;display: grid&lt;/code&gt; is not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="sx"&gt;url('flex-layout.css')&lt;/span&gt; &lt;span class="n"&gt;supports&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The supports-condition is worthy of it's own blog post, because the amount of checks you can do there is absolutely wild. Check out &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@supports#examples"&gt;the examples over at MDN&lt;/a&gt; if you want to see some really cool stuff.&lt;/p&gt;

&lt;h3&gt;
  
  
  Browser support
&lt;/h3&gt;

&lt;p&gt;It seems that &lt;a href="https://caniuse.com/mdn-css_at-rules_import_supports"&gt;only Firefox has shipped support&lt;/a&gt; for &lt;code&gt;supports()&lt;/code&gt; in &lt;code&gt;@import&lt;/code&gt; at the time of writing. And only two weeks ago. Sad trombone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Media queries in &lt;code&gt;@import&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This is one that most developers might actually be familiar with: specifying a media query list to conditionally load CSS. Again some examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="sx"&gt;url('desktop.css')&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1024px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="sx"&gt;url('print.css')&lt;/span&gt; &lt;span class="n"&gt;only&lt;/span&gt; &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="sx"&gt;url('dark.css')&lt;/span&gt; &lt;span class="n"&gt;screen&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's make a mental model of this, as with the previous section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Mental picture only, this does not work and is not valid CSS */&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1024px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="sx"&gt;url('desktop.css')&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="n"&gt;only&lt;/span&gt; &lt;span class="n"&gt;print&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="sx"&gt;url('print.css')&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="n"&gt;screen&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="sx"&gt;url('dark.css')&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Browser support
&lt;/h3&gt;

&lt;p&gt;I can't find any notes for media query list as a separate thing to supported alognside &lt;code&gt;@import&lt;/code&gt;, so I'm going to assume here that support has been around since the early days of CSS imports.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mixing it up
&lt;/h2&gt;

&lt;p&gt;Now we've seen some pretty incredible features of &lt;code&gt;@import&lt;/code&gt;, but we can combine all of them! I don't know if anyone would ever need something like this, but I guess this is something you could do (but not saying you should):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="sx"&gt;url('desktop-fallback.css')&lt;/span&gt; &lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;supports&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="n"&gt;only&lt;/span&gt; &lt;span class="n"&gt;screen&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1024px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c"&gt;/* Mental picture of the above import */&lt;/span&gt;
&lt;span class="k"&gt;@supports&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;only&lt;/span&gt; &lt;span class="n"&gt;screen&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1024px&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;@layer&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;@layer&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c"&gt;/* CSS of desktop-fallback.css here */&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example does not make a ton of sense, but I bet there are some real-world scenarios that we could solve with clever combinations of import conditions and layers. Even more so with the addition of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@supports#function_syntax"&gt;more &lt;code&gt;supports()&lt;/code&gt; capabilities&lt;/a&gt; and added &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media#media_features"&gt;media features&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;See!? I told you! This at-rule is full of good stuff. After diving into this I'm mostly left with questions though.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does anyone actually use this? At what scale?&lt;/li&gt;
&lt;li&gt;Do modern CSS parsers support all these new conditions and layers?&lt;/li&gt;
&lt;li&gt;Will browsers pick up support for &lt;code&gt;supports()&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;What would be a good trigger for us to start using &lt;code&gt;@import&lt;/code&gt; again. There's probably a way to mitigate some of the &lt;a href="https://gtmetrix.com/avoid-css-import.html"&gt;performance drawbacks&lt;/a&gt;, right?&lt;/li&gt;
&lt;li&gt;
&lt;del&gt;I remember seeing a GitHub thread of some CSS working group around adding support for &lt;code&gt;layer&lt;/code&gt; and &lt;code&gt;supports&lt;/code&gt; in the &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; element, but I can't seem to find the relevant issue. If you know where it is, please send me a message, because I think that really fits the theme here as well.&lt;/del&gt;
Thanks for &lt;a href="https://twitter.com/bramus/status/1593376033725591552"&gt;tweeting&lt;/a&gt; Bramus and Barry!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of this is just more research material that I haven't got to yet. I'd love to know more about &lt;code&gt;@import&lt;/code&gt;, so I encourage you to &lt;a href="//mailto:bart@projectwallace.com?subject=CSS%20at-import"&gt;mail&lt;/a&gt; or &lt;a href="https://twitter.com/projectwallace"&gt;tweet&lt;/a&gt; me some good reading material.&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
    </item>
    <item>
      <title>CSS Day 2023 takeaways</title>
      <dc:creator>Bart Veneman</dc:creator>
      <pubDate>Sun, 11 Jun 2023 20:08:56 +0000</pubDate>
      <link>https://forem.com/bartveneman/css-day-2023-takeaways-64c</link>
      <guid>https://forem.com/bartveneman/css-day-2023-takeaways-64c</guid>
      <description>&lt;p&gt;Imagine a conference completely dedicated to CSS. And a former church filled with over 300 CSS enthusiasts. It exists! It's the amazing &lt;a href="https://cssday.nl/2023"&gt;CSS Day conference&lt;/a&gt; and I was lucky enough to attend again this year. During the talks I've been taking notes to see &lt;strong&gt;what all the new trends and techniques mean for authoring and auditing CSS&lt;/strong&gt;. Note: while it may seem skeptical at many points I do think we're living in a golden era of CSS as Una proved in &lt;a href="https://www.youtube.com/watch?v=TVkwiUFbtyQ"&gt;the opening talk&lt;/a&gt; this year.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;CSS Nesting&lt;/li&gt;
&lt;li&gt;CSS Cascade Layers&lt;/li&gt;
&lt;li&gt;CSS Container Queries&lt;/li&gt;
&lt;li&gt;Scroll driven animations and the shorthand trash fire&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  CSS Nesting
&lt;/h2&gt;

&lt;p&gt;CSS Nesting was mentioned in almost every single talk! Even though it doesn't really seem to add any new capabilities to the language itself, everyone seems to agree that the syntactic sugar adds a lot of benefits to the authoring experience. It's not just nesting selectors, but also nesting atrules like &lt;code&gt;@media&lt;/code&gt; inside regular old CSSRules.&lt;/p&gt;

&lt;p&gt;This will change what CSS ships to the browser, because preprocessors (PostCSS, Sass) currently 'unwrap' nested selectors and make it plainly visible how much nesting authors are applying to their selectors. CSS Nesting will make it harder to debug which 'resolved' selector is being applied, so we need browser vendors to level up their devtools to make inspection of nested selectors easier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/projectwallace/css-analyzer/assets/1536852/dc9bbc62-2e6e-480f-8fa2-12af4d2a7a6d"&gt;&lt;br&gt;
    &lt;img alt="Example 'unwrapping' in Chrome Devtools" src="https://res.cloudinary.com/practicaldev/image/fetch/s--TjRZVzPy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/projectwallace/css-analyzer/assets/1536852/dc9bbc62-2e6e-480f-8fa2-12af4d2a7a6d" width="800" height="360"&gt;&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the example above you can see that Chrome Devtools (Chrome 114) are trying to add helpful context about the selector, but there's multiple levels of nesting, so we don't get to see the full resolved selector here, which is inconvenient.&lt;/p&gt;

&lt;p&gt;Time will tell how much profit CSS nesting will bring us. Writing the CSS will become faster, but debugging it will become harder as it requires more tooling or mental gymnastics to surface the resolved CSS.&lt;/p&gt;


    &lt;p&gt;Project Wallace should definitely help you figure out the complexity of your CSS, even when CSS nesting is present. It will involve a structural overhaul of the core of our &lt;a href="https://github.com/projectwallace/css-analyzer"&gt;CSS analyzer&lt;/a&gt;, but I feel strongly about this and our analysis should be able to help calculate Selector and AtRule complexity, even in complex situations.&lt;/p&gt;

&lt;h2&gt;
  
  
  CSS Cascade Layers
&lt;/h2&gt;

&lt;p&gt;Many talks mentioned CSS Cascade Layers as a good way to manage specificity and the cascade. And it is! It's such a useful addition to CSS that will help us avoid overly complex selectors and resorting to &lt;code&gt;!important&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Layers can be nested, it's even a very nice method to group certain parts of the CSS, like a sort of namespace. But, like CSS Nesting, it brings with it some difficulties in inspecting the final CSS. A declaration can be nested several layers deep, so to debug why certain properties are applied we need some sort of view of the resolved layer tree. Luckily, both Firefox and Chrome devtools are already on the case, but with different levels of completeness.&lt;/p&gt;


  See the Pen &lt;a href="https://codepen.io/bartveneman/pen/xxQGPjB"&gt;
  @layer resolution&lt;/a&gt; by Bart Veneman (&lt;a href="https://codepen.io/bartveneman"&gt;@bartveneman&lt;/a&gt;)
  on &lt;a href="https://codepen.io"&gt;CodePen&lt;/a&gt;.



    &lt;a href="https://github.com/projectwallace/css-analyzer/assets/1536852/537214c9-f04c-4ca7-8c01-49651b61a877"&gt;
        &lt;img alt="Firefox (v113) devtools showing the cascade layers involved" src="https://res.cloudinary.com/practicaldev/image/fetch/s--MsBhILLN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/projectwallace/css-analyzer/assets/1536852/537214c9-f04c-4ca7-8c01-49651b61a877" width="800" height="454"&gt;
    &lt;/a&gt;Firefox (v113) devtools showing the cascade layers involved
    



    &lt;a href="https://github.com/projectwallace/css-analyzer/assets/1536852/6b96a717-080d-4ac5-a2b1-abb440a8809c"&gt;
        &lt;img alt="Chrome (v114) devtools showing the resolved layers as well as the resolved layer tree of all layers in the CSS." src="https://res.cloudinary.com/practicaldev/image/fetch/s--FrUs-5wU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/projectwallace/css-analyzer/assets/1536852/6b96a717-080d-4ac5-a2b1-abb440a8809c" width="800" height="607"&gt;
    &lt;/a&gt;Chrome (v114) devtools showing the resolved layers as well as the resolved layer tree of all layers in the CSS.
    


&lt;p&gt;Chrome showing the full layer tree is something I very much appreciate, because it will help you visualize the high-level architecture of your CSS.&lt;/p&gt;


    &lt;p&gt;Wallace should add support for showing the layer tree as well and it's been something I've been thinking of for over a year now, because &lt;a href="https://www.bram.us/"&gt;Bramus&lt;/a&gt; showed &lt;a href="https://slidr.io/bramus/the-css-cascade-a-deep-dive-2022-06-09-css-day#85" rel="noreferrer"&gt;this excellent visual&lt;/a&gt; last year at the same conference (!).&lt;/p&gt;

    
        &lt;a href="https://github.com/projectwallace/css-analyzer/assets/1536852/63fc4fbb-5074-4217-842d-b2b1d02eaf4f"&gt;
            &lt;img alt="Screenshot of Bramus' slide of last year's talk about CSS cascade layers, showing a stacked bar chart of colord blocks, each representing a distinct CSS layer atrule, proportionally sized to the amount of rules/declarations inside them." src="https://res.cloudinary.com/practicaldev/image/fetch/s--mpKxapVN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/projectwallace/css-analyzer/assets/1536852/63fc4fbb-5074-4217-842d-b2b1d02eaf4f" width="800" height="471"&gt;
        &lt;/a&gt;
        This visual has been on my mind all year and I hope we can bring it to this silly little website very soon!
    


&lt;h2&gt;
  
  
  CSS Container Queries
&lt;/h2&gt;

&lt;p&gt;Container queries will change our code from mostly writing &lt;code&gt;@media&lt;/code&gt; to writing &lt;code&gt;@container&lt;/code&gt; to proportionally size our layouts and components. &lt;a href="https://www.miriamsuzanne.com/"&gt;Miriam Suzanne&lt;/a&gt; &lt;a href="https://www.youtube.com/watch?v=-Fw8GSksUIo&amp;amp;list=PLjnstNlepBvOG299LOrvMFJ8WreCDWWd4&amp;amp;index=9"&gt;articulated quite well&lt;/a&gt; that it is very likely that we'll use &lt;code&gt;@container&lt;/code&gt; for sizing elements and use &lt;code&gt;@media&lt;/code&gt; to make decisions based on the OS level, like dark mode, and reduced motion.&lt;/p&gt;


    &lt;a href="https://github.com/projectwallace/css-analyzer/assets/1536852/9c02fd33-a0c5-4e36-8d85-36f72bbb7534"&gt;
        &lt;img alt="Example Firefox devtools for container query" src="https://res.cloudinary.com/practicaldev/image/fetch/s--au-bNFu0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/projectwallace/css-analyzer/assets/1536852/9c02fd33-a0c5-4e36-8d85-36f72bbb7534" width="800" height="358"&gt;
    &lt;/a&gt;Firefox devtools shows the actual size of the container element which makes for easy debugging, as well as showing the &lt;code&gt;container-type&lt;/code&gt;, &lt;code&gt;container-name&lt;/code&gt; and size query.
    



    &lt;a href="https://github.com/projectwallace/css-analyzer/assets/1536852/b8794625-a644-4f8d-8fa1-e6a822bd224c"&gt;
        &lt;img alt="Example Chrome devtools for container query" src="https://res.cloudinary.com/practicaldev/image/fetch/s--0kjCpxkj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/projectwallace/css-analyzer/assets/1536852/b8794625-a644-4f8d-8fa1-e6a822bd224c" width="800" height="303"&gt;
    &lt;/a&gt;Chrome devtools shows the &lt;code&gt;container-name&lt;/code&gt;, &lt;code&gt;container-type&lt;/code&gt; and size query.
    


&lt;p&gt;It looks like Firefox is very much on top of the game here, although Chrome's devtools are &lt;em&gt;perfectly&lt;/em&gt; usable as well.&lt;/p&gt;


    &lt;p&gt;One thing I would add to our own analyzer is to write detection for unused &lt;code&gt;container-name&lt;/code&gt;s and unknown named &lt;code&gt;@container&lt;/code&gt;s. We warn for empty CSS Rules, so why not unused/undeclared container names?&lt;/p&gt;

&lt;h2&gt;
  
  
  Scroll driven animations and the shorthand trash fire
&lt;/h2&gt;

&lt;p&gt;A couple of demos and talks showed off the amazing things we can build with Scroll Driven Animations. I'm not going to explain them here, so if you need examples you can check the ones from &lt;a href="https://jhey.dev/cheep/rotating-gallery-with-css-scroll-driven-animations/"&gt;Jhey&lt;/a&gt; or &lt;a href="https://www.bram.us/2023/05/16/whats-new-in-web-animations/"&gt;Bramus&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One thing that stood out is that the new &lt;code&gt;animation-timeline&lt;/code&gt;, &lt;code&gt;animation-composition&lt;/code&gt; and &lt;code&gt;animation-range&lt;/code&gt; properties &lt;a href="https://twitter.com/bramus/status/1667916024107216897"&gt;cannot be combined&lt;/a&gt; with the &lt;code&gt;animation&lt;/code&gt;-shorthand property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* WARNING: will not work */&lt;/span&gt;
&lt;span class="nf"&gt;#square&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3s&lt;/span&gt; &lt;span class="n"&gt;demoAnimation&lt;/span&gt; &lt;span class="n"&gt;alternate&lt;/span&gt; &lt;span class="nb"&gt;scroll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;block&lt;/span&gt; &lt;span class="n"&gt;nearest&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* This works */&lt;/span&gt;
&lt;span class="nf"&gt;#square&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;animation-name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;demoAnimation&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;animation-duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;animation-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;alternate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;animation-timeline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;scroll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;block&lt;/span&gt; &lt;span class="n"&gt;nearest&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Or, if you're a fan of shorthand, this works too */&lt;/span&gt;
&lt;span class="nf"&gt;#square&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3s&lt;/span&gt; &lt;span class="n"&gt;demoAnimation&lt;/span&gt; &lt;span class="n"&gt;alternate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;animation-timeline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;scroll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;block&lt;/span&gt; &lt;span class="n"&gt;nearest&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding that extra property to the &lt;code&gt;animation&lt;/code&gt; shorthand would have made it a bit difficult, if not impossible, to parse correctly. Or as Tab Atkins said it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;q&gt;the animation shorthand is already a trash fire of parsing&lt;/q&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://github.com/w3c/csswg-drafts/issues/8054#issuecomment-1476502277"&gt;CSS Working Group meeting notes, 20-03-2023&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This problem strengthens my belief that &lt;a href="https://csswizardry.com/2016/12/css-shorthand-syntax-considered-an-anti-pattern/"&gt;CSS shorthands are an anti-pattern&lt;/a&gt; and should be avoided in most cases. Maybe we should even go so far as actively warning against them on this website as they make auditing your CSS more complex too. Anyway, this could easily be a blog post in itself…&lt;/p&gt;




&lt;p&gt;CSS is moving at rocket-speed pace and I'm a big fan of all the attention it's getting from all browser vendors. There's &lt;a href="https://hacks.mozilla.org/2023/02/announcing-interop-2023/"&gt;more tools coming in our browsers on a daily basis&lt;/a&gt; and I'm so grateful to all browser and devtools teams for their hard work.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building a lightweight CSS formatter</title>
      <dc:creator>Bart Veneman</dc:creator>
      <pubDate>Sun, 11 Jun 2023 20:02:20 +0000</pubDate>
      <link>https://forem.com/bartveneman/building-a-lightweight-css-formatter-m3p</link>
      <guid>https://forem.com/bartveneman/building-a-lightweight-css-formatter-m3p</guid>
      <description>&lt;p&gt;Last year we introduced a prettifier to this website, because it's one of those things you often want to do when auditing CSS. Then, not long after that, we added the option to prettify your CSS before analyzing it. Because we have a DevTools panel now on the analyzer page, it makes sense to view the CSS usage in a formatted manner. But this came at a cost: prettifying the CSS took up to twice as long analyzing the CSS! Time to look at a more performant alternative.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;img alt="Example output of formatted CSS that we need to audit" src="https://user-images.githubusercontent.com/1536852/244181349-9824fdf7-abfe-45f5-b2c4-859d87f7e8d1.png"&amp;gt;
&amp;lt;figcaption&amp;gt;CSS that is being audited where the source code was originally minified, making the auditing process difficult, because those lines become pretty much unreadable.&amp;lt;/figcaption&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  The naive way: Prettier
&lt;/h2&gt;

&lt;p&gt;The first iteration of our prettifier used &lt;a href="https://prettier.io/"&gt;Prettier&lt;/a&gt;, a very popular and respectable project that can pretty-print hundreds of different languages. Even before testing I was already pretty sure this was going to be a slow function call, so I went ahead and made sure to only import the relevant modules &lt;em&gt;and&lt;/em&gt; to put the function in a WebWorker to run it off the UI thread.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// prettify-worker.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;prettier&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prettier/esm/standalone.mjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;cssParser&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prettier/esm/parser-postcss.mjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// The `event` here is the message we send from the UI to&lt;/span&gt;
&lt;span class="c1"&gt;// the worker and `event.data` contains the string of CSS.&lt;/span&gt;
&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;prettier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cssParser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="c1"&gt;// Send result back to UI thread&lt;/span&gt;
        &lt;span class="nx"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This worked quite well, but after some weeks I noticed more often that the progress back on the analyzer page got stuck on &lt;em&gt;"prettifying CSS"&lt;/em&gt; step. It's not necessarily a bad thing, but if you analyze CSS as much as I do it becomes annoying after some time. And since prettifying CSS isn't even our core business (if you can call it that), it's even more frustrating.&lt;/p&gt;

&lt;h2&gt;
  
  
  CSSTree to the rescue
&lt;/h2&gt;

&lt;p&gt;After thinking about the problem for a while I realised that I could enlist the help of &lt;a href="https://github.com/csstree/csstree"&gt;CSSTree&lt;/a&gt; to do some of the work. The CSS Analyzer is based on CSSTree's AST, so I know how the thing works and the dependency is already on the page, so no need to download more dependencies. Prettier + Postcss cost almost 340kB to download, which isn't huge, but it would be nice if we could reduce that amount.&lt;/p&gt;

&lt;p&gt;So how do you turn a string of (potentially minified) CSS into a string of mostly readable CSS with CSSTree? Let's start by parsing the CSS, so we get an AST to work with. Then, using that AST, we apply our knowledge of CSS structure to turn them into readable strings, line by line.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fast CSS parsing
&lt;/h3&gt;

&lt;p&gt;CSSTree has some neat parsing options to speed things up a bit. It allows you to skip certain tokens, which will reduce memory usage and all that. The following script creates an AST of our CSS. An example of such an AST can be inspected on &lt;a href="https://astexplorer.net/#/gist/0619055be1fcbec410702a63db37806f/59bddd15b3446ba5b4bb5878647c6584b4feb2cf"&gt;ASTExplorer&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;ast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;positions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;parseAtrulePrelude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;parseCustomProperty&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;parseValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can skip parsing Atrule preludes, custom properties and values, because we're only interested in their 'raw' string values, not the deeper tokens within them. We do need &lt;code&gt;positions&lt;/code&gt;, because this will allow us to do a lot of &lt;code&gt;css.substring(x, y)&lt;/code&gt; later on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a readable string of CSS
&lt;/h2&gt;

&lt;p&gt;With the AST in hand we can begin thinking about how to turn it into pretty-looking CSS. We need the CSS to be pretty enough to show it in a readable way in our DevTools, not any fancier than that. After some thinking I came up with the following rules:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Every &lt;strong&gt;AtRule&lt;/strong&gt; starts on a new line&lt;/li&gt;
&lt;li&gt;Every &lt;strong&gt;Rule&lt;/strong&gt; starts on a new line&lt;/li&gt;
&lt;li&gt;Every &lt;strong&gt;Selector&lt;/strong&gt; starts on a new line&lt;/li&gt;
&lt;li&gt;A comma is placed after every &lt;strong&gt;Selector&lt;/strong&gt; that's not the last in the &lt;strong&gt;SelectorList&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Every &lt;strong&gt;Block&lt;/strong&gt; (&lt;code&gt;{}&lt;/code&gt;) is indented with 1 tab more than the previous indentation level&lt;/li&gt;
&lt;li&gt;Every &lt;strong&gt;Declaration&lt;/strong&gt; starts on a new line&lt;/li&gt;
&lt;li&gt;Every &lt;strong&gt;Declaration&lt;/strong&gt; ends with a semicolon (&lt;code&gt;;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;An empty line is placed after a &lt;strong&gt;Block&lt;/strong&gt;, unless it's the last in the surrounding block&lt;/li&gt;
&lt;li&gt;Unknown syntax is rendered as-is&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As you can see from this list, we're dealing with a very limited subset of CSS tokens here: Stylesheet, Atrule, Rule, SelectorList, Selector, Block and Declaration. We're starting our formatting from the Stylesheet level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;indent_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;child&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Rule&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;print_rule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;indent_level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Atrule&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;print_atrule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;indent_level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;print_unknown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;indent_level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This kicks of our prettification, calling &lt;code&gt;print_atrule&lt;/code&gt; and &lt;code&gt;print_rule&lt;/code&gt;, which look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;print_atrule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;indent_level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;indent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;indent_level&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Block&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;print_block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;indent_level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// `@import url(style.css);` has no block, neither does `@layer layer1;`&lt;/span&gt;
        &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;print_rule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;indent_level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prelude&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SelectorList&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;print_selectorlist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;indent_level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Block&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;print_block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;indent_level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this goes on a bit for all the other types as well. There's even some recursion in here, because Atrules can be nested (&lt;code&gt;@media&lt;/code&gt; in &lt;code&gt;@layer&lt;/code&gt; and CSS nesting, to name a few), so we need to make sure to account for those as well.&lt;/p&gt;

&lt;p&gt;If you want to see more: the &lt;a href="https://github.com/projectwallace/format-css/blob/main/index.js"&gt;source code&lt;/a&gt; for this can be found &lt;a href="https://github.com/projectwallace/format-css"&gt;on GitHub&lt;/a&gt;, where I'm currently in the process of making this a standalone NPM package.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tradeoffs
&lt;/h2&gt;

&lt;p&gt;No project is perfect and neither is this little script. There's some things that it does well and in a simple fashion, but some things I'll consider beyond the scope and necessity of this package. And that's ok, because we just need it to format your CSS well enough to audit it easily.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ super fast (&amp;gt;90% faster than Prettier)&lt;/li&gt;
&lt;li&gt;✅ 'tiny' bundle size (~99% smaller than Prettier)&lt;/li&gt;
&lt;li&gt;✅ prettifies well enough for our use-cases&lt;/li&gt;
&lt;li&gt;🔸 if your source CSS renders things multi-line (like long selectors or values), they'll stay multi-line (fine, I guess)&lt;/li&gt;
&lt;li&gt;🔺 not as configurable and extensive as Prettier&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are tradeoffs I can live with.&lt;/p&gt;

</description>
      <category>css</category>
      <category>prettier</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>A deep dive into the CSS of Instagram.com</title>
      <dc:creator>Bart Veneman</dc:creator>
      <pubDate>Mon, 06 Mar 2023 13:07:42 +0000</pubDate>
      <link>https://forem.com/bartveneman/a-deep-dive-into-the-css-of-instagramcom-3bfa</link>
      <guid>https://forem.com/bartveneman/a-deep-dive-into-the-css-of-instagramcom-3bfa</guid>
      <description>&lt;p&gt;Instagram.com ranks pretty high according to the Alexa top visited websites. This means that all the CSS from instagram.com gets downloaded to millions of devices every day. Let's have a look at the interesting parts of it their CSS analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  First impressions
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tr&gt;
&lt;td&gt;Filesize&lt;/td&gt;
&lt;td&gt;543 kB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source Lines of Code&lt;/td&gt;
&lt;td&gt;27,992&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rules&lt;/td&gt;
&lt;td&gt;10,314&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Selectors&lt;/td&gt;
&lt;td&gt;10,449&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Declarations&lt;/td&gt;
&lt;td&gt;16,235&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Coming in at 543 kB, the overall CSS size is pretty substantial. It's not as bad as some other large companies, but I am almost certain it could be way less. With the amount of visitors they have, making their CSS smaller would be a nice bandwidth-saving as well.&lt;/p&gt;

&lt;p&gt;Almost 30,000 &lt;a href="https://www.projectwallace.com/blog/counting-lines-of-code-in-css"&gt;Source Lines of Code&lt;/a&gt;, with declarations making up for roughly 50% of that (16,235). There are 10,449 selectors, which means that the remaining ~4000 lines of code is hidden in at-rules! That will be an interesting one to look at later on in this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  CSS Rules
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--L2RYoNb9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/1536852/220675099-ca09b6d8-6b02-42c7-b096-709645c1ccab.png" class="article-body-image-wrapper"&gt;&lt;img alt="Ruleset sizes of Instagram.com" src="https://res.cloudinary.com/practicaldev/image/fetch/s--L2RYoNb9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/1536852/220675099-ca09b6d8-6b02-42c7-b096-709645c1ccab.png" width="880" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the first things that jumps out is a massive rule size of 550! Zooming in on that we see a rule with 2 selectors 548 declarations! This is not uncommon since the rise of CSS custom properties, because lots of sites declare most of their 'variables' on the &lt;code&gt;:root&lt;/code&gt; selector. 548 custom properties is just... more than usual. In the graph above this ruleset is visible as the top left dot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.__ig-light-mode&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--fds-black&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#000000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--fds-black-alpha-05&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;--fds-black-alpha-10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;--fds-black-alpha-15&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;--fds-black-alpha-20&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;--fds-black-alpha-30&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;--fds-black-alpha-40&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;--fds-black-alpha-50&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;--fds-black-alpha-60&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;--fds-black-alpha-80&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;--fds-blue-05&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ecf3ff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--fds-blue-30&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#aac9ff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--fds-blue-40&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#77a7ff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--fds-blue-60&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#1877f2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--fds-blue-70&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#2851a3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--fds-blue-80&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#1d3c78&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--fds-button-text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#444950&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--fds-comment-background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f2f3f5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;/* hundreds more, etc. */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Right after that one, the dark theme:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.__ig-dark-mode&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--fds-black&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--fds-black-alpha-05&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;--fds-black-alpha-10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;--fds-black-alpha-15&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;--fds-black-alpha-20&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;--fds-black-alpha-30&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;--fds-black-alpha-40&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;--fds-black-alpha-50&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;--fds-black-alpha-60&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;--fds-black-alpha-80&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;--fds-blue-05&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--fds-blue-30&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;/* hundreds more */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Selectors
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6r1O_vam--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/1536852/222164192-2bea3dd3-9fe0-4f1f-afe5-e2fad0b3e86d.png" class="article-body-image-wrapper"&gt;&lt;img alt="Instagram.com Selector analysis on projectwallace.com" src="https://res.cloudinary.com/practicaldev/image/fetch/s--6r1O_vam--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/1536852/222164192-2bea3dd3-9fe0-4f1f-afe5-e2fad0b3e86d.png" width="880" height="568"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A surprisingly good score for instagram here: 11,573 selectors with 96.16% being unique. Most websites with such a vast amount of selectors suffer from a lot of duplication.&lt;/p&gt;

&lt;p&gt;On the complexity side of things it's looking good as well: less than 200 selectors have a higher complexity than 3, which is pretty good.&lt;/p&gt;

&lt;p&gt;The most surprising part for me was the specificity: over 10,000 selectors have specificity of either &lt;code&gt;[0,1,0]&lt;/code&gt; or &lt;code&gt;[0,2,0]&lt;/code&gt;. And even the maximum specificity (&lt;code&gt;[1,3,0]&lt;/code&gt;) is way below the maximum of what many sites of this size have. Good job!&lt;/p&gt;

&lt;h2&gt;
  
  
  At-rules
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;@media&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The first thing that jumps out when checking the at-rules are the &lt;code&gt;@media&lt;/code&gt; queries. It's not just the total of 1,600+, but the fact that there's 135 unique ones. 135 unique media queries! Pretty much all of them use &lt;code&gt;px&lt;/code&gt; as units for &lt;code&gt;min-width&lt;/code&gt; or &lt;code&gt;max-width&lt;/code&gt; and most of them are really, really specific numbers. I wonder if they have some kind of system for that, or that they just eyeball the page layout and apply media queries where they see fit.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
        &lt;thead&gt;
            &lt;tr&gt;
                &lt;th&gt;Query&lt;/th&gt;
                &lt;th&gt;Count&lt;/th&gt;
            &lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;
            &lt;tr&gt;
                &lt;td&gt;&lt;code&gt;(max-width: 767px)&lt;/code&gt;&lt;/td&gt;
                &lt;td&gt;340&lt;/td&gt;
            &lt;/tr&gt;
            &lt;tr&gt;
                &lt;td&gt;&lt;code&gt;(min-width: 1025px)&lt;/code&gt;&lt;/td&gt;
                &lt;td&gt;321&lt;/td&gt;
            &lt;/tr&gt;
            &lt;tr&gt;
                &lt;td&gt;&lt;code&gt;(min-width: 768px) and (max-width: 1024px)&lt;/code&gt;&lt;/td&gt;
                &lt;td&gt;319&lt;/td&gt;
            &lt;/tr&gt;
            &lt;tr&gt;
                &lt;td&gt;&lt;code&gt;(max-width: 899px)&lt;/code&gt;&lt;/td&gt;
                &lt;td&gt;52&lt;/td&gt;
            &lt;/tr&gt;
            &lt;tr&gt;
                &lt;td&gt;&lt;code&gt;(min-width: 900px)&lt;/code&gt;&lt;/td&gt;
                &lt;td&gt;20&lt;/td&gt;
            &lt;/tr&gt;
            &lt;tr&gt;
                &lt;td&gt;&lt;code&gt;(max-width: 900px)&lt;/code&gt;&lt;/td&gt;
                &lt;td&gt;14&lt;/td&gt;
            &lt;/tr&gt;
            &lt;tr&gt;
                &lt;td&gt;&lt;code&gt;(max-width: 905px)&lt;/code&gt;&lt;/td&gt;
                &lt;td&gt;3&lt;/td&gt;
            &lt;/tr&gt;
        &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Excerpt from instagram.com's &lt;code&gt;@media&lt;/code&gt; at-rules. Note the almost-but-not-quite-identical breakpoints around 900px.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;@keyframes&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Another interesting metric is the 140 unique &lt;code&gt;@keyframes&lt;/code&gt; that are in Instagram's CSS, which is a lot. Usually this can be explained by the fact that many of the atrules are straight copies because of vendor prefixes in the atrule, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@keyframes thing {
    from {}
    to {}
}

@-webkit-keyframes thing {
    from {}
    to {}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... but this is not the case on instagram.com. There are truly 140 unique &lt;code&gt;@keyframes&lt;/code&gt; rules, most of which have a random string as a name, so I'm happy that I'm not the one having go debug that part of their CSS.&lt;/p&gt;

&lt;pre&gt;@keyframes x104yahw-B
@keyframes x1078a53-B
@keyframes x13f3g3z-B
@keyframes x148q4be-B
@keyframes x166kt1m-B&lt;/pre&gt;
Excerpt from Instagram's 140 unique &lt;code&gt;@keyframes&lt;/code&gt; with randomly generated names.



&lt;h2&gt;
  
  
  Declarations
&lt;/h2&gt;

&lt;p&gt;Only one thing worth mentioning here: there are only 167 &lt;code&gt;!important&lt;/code&gt;s, which is really low compared to the total of 17,356 declarations. Less than 1% &lt;code&gt;!important&lt;/code&gt; usage, that's something that not many websites at scale are able to pull off!&lt;/p&gt;

&lt;h2&gt;
  
  
  Properties
&lt;/h2&gt;

&lt;p&gt;I think Instagram must have broken some kind of record here for having 548 unique custom properties. That's just bonkers. Other than that, the top usage ones are the usual suspects, with &lt;code&gt;width&lt;/code&gt;, &lt;code&gt;height&lt;/code&gt;, &lt;code&gt;background-color&lt;/code&gt; and &lt;code&gt;display&lt;/code&gt; ranking high, just like most other websites.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VdPb513I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/1536852/222169470-6ebd52b1-4a48-43a5-af6f-95be6cbbc202.png" class="article-body-image-wrapper"&gt;&lt;img alt="CSS properties on instagram.com as analyzed on projectwallace.com" src="https://res.cloudinary.com/practicaldev/image/fetch/s--VdPb513I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/1536852/222169470-6ebd52b1-4a48-43a5-af6f-95be6cbbc202.png" width="880" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One thing that's cool about checking someone else's CSS is that you can learn a lot by doing it. Today I learned about &lt;a href="https://udn.realityripple.com/docs/Archive/Web/CSS/-ms-scroll-rails"&gt;&lt;code&gt;-ms-scroll-rails&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://udn.realityripple.com/docs/Archive/Web/CSS/-ms-scroll-chaining"&gt;&lt;code&gt;-ms-scroll-chaining&lt;/code&gt;&lt;/a&gt;, which are obsolete properties, but apparently still used on today's instagram.com.&lt;/p&gt;

&lt;h2&gt;
  
  
  Values
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Colors
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w1oQRKZs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/1536852/222173478-605210c9-4e95-4dac-b359-920d62aba6a8.png" class="article-body-image-wrapper"&gt;&lt;img alt="Project Wallace report on CSS colors on instagram.com" src="https://res.cloudinary.com/practicaldev/image/fetch/s--w1oQRKZs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/1536852/222173478-605210c9-4e95-4dac-b359-920d62aba6a8.png" width="880" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;539 unique&lt;/strong&gt; colors. That just can't be right. We've seen the vast amount of custom properties above, so I'm surprised that there's still so many unique colors. I'm afraid that there must be a part of Instagram that's old and tucked away and not updated with the same level of care as the rest of the site is.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5qXEGlIP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/1536852/222172597-f4e1619f-4af6-48fb-8e8c-84651b715da0.png" class="article-body-image-wrapper"&gt;&lt;img alt="Project Wallace report on CSS color format usage on instagram.com" src="https://res.cloudinary.com/practicaldev/image/fetch/s--5qXEGlIP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/1536852/222172597-f4e1619f-4af6-48fb-8e8c-84651b715da0.png" width="880" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An insteresting note about Instagram's color format usage: they seem to use more different formats than most sites I've seen so far. I'm surprised that there are so many named colors in there.&lt;/p&gt;

&lt;h3&gt;
  
  
  Font-families
&lt;/h3&gt;

&lt;p&gt;Again, a huge list: 78 unique font-families (most sites stop at about ~20). Scrolling through that actually reveals that there's probably a good reason for this amount, because some of themhave names like &lt;code&gt;'Fix for Mac Chrome 80'&lt;/code&gt; and I think that &lt;code&gt;font-family&lt;/code&gt; alone is worth a blog post on itself.&lt;/p&gt;




&lt;p&gt;That's it for now! I won't go into more details about other things because A) this post is long enough as it is and B) most things are what you would expect from a website as big as instagram.com.&lt;/p&gt;

&lt;p&gt;I highly encourage you to check out the report yourself and see what you can take away from these analytics. Let me know what you think of this type of post! Did you like it? Was it deep enough? Or still too superficial? Ping me at &lt;a href="https://twitter.com/bartveneman"&gt;@bartveneman&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post is the first part of a series of posts focusing on disecting the CSS of popular websites. The goal is to see how big companies structure their CSS, what mistakes they make and what we can learn from their CSS architecture.&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>codequality</category>
    </item>
    <item>
      <title>Project Wallace 2022 in Review</title>
      <dc:creator>Bart Veneman</dc:creator>
      <pubDate>Fri, 30 Dec 2022 10:09:41 +0000</pubDate>
      <link>https://forem.com/bartveneman/project-wallace-2022-in-review-2p8j</link>
      <guid>https://forem.com/bartveneman/project-wallace-2022-in-review-2p8j</guid>
      <description>&lt;p&gt;My &lt;a href="https://www.projectwallace.com/blog/2021-review-in-numbers"&gt;review of 2021&lt;/a&gt; generated lots of encouraging comments. Here's what happened with Project Wallace in 2022:&lt;/p&gt;

&lt;h2&gt;
  
  
  Notable releases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.projectwallace.com/css-code-quality"&gt;CSS Code Quality&lt;/a&gt; is the biggest release in terms of complexity and amount of usage. Read more about it in &lt;a href="https://dev.to/blog/new-online-css-code-quality-analyzer"&gt;the introduction post&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.projectwallace.com/prettify-css"&gt;Online CSS Prettifier&lt;/a&gt; is a tool that already exists on a lot of other websites, but I don't like having to google my way out of this every time, so &lt;a href="https://www.projectwallace.com/blog/prettify-css-online"&gt;I decided&lt;/a&gt; Project Wallace should have it's own prettifier.&lt;/li&gt;
&lt;li&gt;The core &lt;a href="https://github.com/projectwallace/css-analyzer"&gt;CSS Analyzer&lt;/a&gt; (please star it on GitHub!) has seen 10 releases this year in total:

&lt;ul&gt;
&lt;li&gt;Support for &lt;a href="https://github.com/projectwallace/css-analyzer/pull/215"&gt;analyzing Embedded Content&lt;/a&gt; in CSS, like base64 encoded fonts or images;&lt;/li&gt;
&lt;li&gt;Lots of &lt;a href="http://browserhacks.com/"&gt;browserhacks&lt;/a&gt; &lt;a href="https://github.com/projectwallace/css-analyzer/pull/258"&gt;are now analyzed&lt;/a&gt;, ranging from at-rules and selectors to properties and values;&lt;/li&gt;
&lt;li&gt;The relatively new &lt;code&gt;@layer&lt;/code&gt; at-rule &lt;a href="https://github.com/projectwallace/css-analyzer/pull/221"&gt;is now analyzed&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/projectwallace/css-analyzer/pull/217"&gt;Total bundle size went down a lot&lt;/a&gt; because CSSTree now supports tree shaking!&lt;/li&gt;
&lt;li&gt;Lots of performance improvements and less overall memory usage, despite adding more and more features.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Traffic
&lt;/h2&gt;

&lt;p&gt;Although the amount of visitors seems to have doubled, the amount of pageviews has only increased slightly. Part of this is due to an error on my end where analytics was broken on the Projects side, but still it's a remarkable difference in visitors vs. pageviews ratio.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;2020&lt;/th&gt;
&lt;th&gt;2021&lt;/th&gt;
&lt;th&gt;2022&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Visitors&lt;/td&gt;
&lt;td&gt;6,510&lt;/td&gt;
&lt;td&gt;10,942&lt;/td&gt;
&lt;td&gt;20,577&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Page views&lt;/td&gt;
&lt;td&gt;24,767&lt;/td&gt;
&lt;td&gt;40,256&lt;/td&gt;
&lt;td&gt;46,689&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I'm starting to think that there's less 'quality' traffic to the site, and more one-off visitors. According to Fathom the bounce rate is slightly higher than last year, so I think there are more people coming for a handful of pages and then moving along with their day. At first sight this frustrated me a little, but on second thought I think that's a good thing: people come here to accomplish a task and then get on with the work they intended to do. Project Wallace is not a social media platform, we don't need high engagement, we need high quality tools to do our work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Popular pages
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://www.projectwallace.com//analyze-css"&gt;CSS Analyzer&lt;/a&gt; is still on top in terms of page views, partially thanks to &lt;a href="https://coliss.com/articles/build-websites/operation/css/css-analyzer-by-projectwallace.html"&gt;coliss.com&lt;/a&gt;, but the gap is becoming smaller with...&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.projectwallace.com//css-code-quality"&gt;CSS Code Quality page&lt;/a&gt; is catching up in terms of popularity quickly. It has been mentioned of several websites like &lt;a href="https://habr.com/ru/company/htmlacademy/blog/677318/"&gt;habr.com&lt;/a&gt;, &lt;a href="http://kachibito.net/useful-resource/css-code-quality"&gt;kachibito.net&lt;/a&gt; and many others.&lt;/li&gt;
&lt;li&gt;By far the best blog post ever I've written: &lt;a href="https://www.projectwallace.com/blog/css-complexity"&gt;CSS Complexity: it's complicated&lt;/a&gt; peaks at 6,000+ page views at the time of writing and it's still being visited every single day.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Website activity
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;2020&lt;/th&gt;
&lt;th&gt;2021&lt;/th&gt;
&lt;th&gt;2022&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Analyze CSS (url)&lt;/td&gt;
&lt;td&gt;742&lt;/td&gt;
&lt;td&gt;3,161&lt;/td&gt;
&lt;td&gt;6,341&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Analyze CSS (raw input)&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;503&lt;/td&gt;
&lt;td&gt;2,327&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSS Code Quality (url)&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;4,762&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSS Code Quality (raw input)&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;758&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scrape CSS&lt;/td&gt;
&lt;td&gt;138&lt;/td&gt;
&lt;td&gt;264&lt;/td&gt;
&lt;td&gt;248&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prettify CSS&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;37&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It's amazing to see that there's such an interest in CSS Code Quality tooling. The funny part is that I started writing it to help someone explain how the plain CSS analytics could be translated to 'quality'. Apparently there are more people struggling with the raw output of their CSS analytics. And I get it. Having the data is one, but understanding what it means is something different.&lt;br&gt;
The CSS Code Quality package is still very crude, so I'm planning on some additions to help explain some of the concepts using the actual CSS that was analyzed. That should give everyone (myself included!) a better understanding on why some rankings are under-performing.&lt;/p&gt;

&lt;p&gt;It's no surprise that the CSS Prettifier isn't used very often. What &lt;em&gt;does&lt;/em&gt; surprise me is that the CSS Scraper is so much less popular than the rest. I expected more people to be in need of a tool like this. Perhaps this is because the link is not in the site header. Ultimately, everyone analyzing CSS by URL uses this feature under the hood, so the actual total is more in the 11,000+ range, which is bonkers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ideas for the new year
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Update the CSS Analyzer to use &lt;a href="https://github.com/bramus/specificity"&gt;Bramus' Specificity calculator&lt;/a&gt;. It's more correct and saves me a bunch of work in maintaining;&lt;/li&gt;
&lt;li&gt;Adding a page to the website to quickly calculate specificity. There's already &lt;a href="https://isellsoap.github.io/specificity-visualizer/"&gt;a bunch&lt;/a&gt; &lt;a href="https://polypane.app/css-specificity-calculator/"&gt;of them&lt;/a&gt; &lt;a href="https://specificity.keegan.st/"&gt;out there&lt;/a&gt;, but like the prettifier, I don't like context switching;&lt;/li&gt;
&lt;li&gt;Improving the &lt;a href="https://www.projectwallace.com/css-code-quality"&gt;CSS Code Quality&lt;/a&gt; page to use the analyzed CSS to explain the bits that are sub-optimal;&lt;/li&gt;
&lt;li&gt;Implementing lots of new analysis features, like native CSS nesting, reporting on color formats used and the most important: &lt;a href="https://github.com/projectwallace/css-analyzer/issues/218"&gt;calculating total CSS complexity&lt;/a&gt;!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;I'm pretty happy with these numbers, but even more happy with the feedback that comes from the CSS community in many forms. A part of that community was at &lt;a href="https://cssday.nl/2022/schedule"&gt;CSS Day Conference&lt;/a&gt; this year and I was lucky to meet some inspiring people there, like &lt;a href="https://twitter.com/bramus"&gt;Bramus&lt;/a&gt; and &lt;a href="https://twitter.com/pepelsbey_dev"&gt;Vadim&lt;/a&gt;. Also, &lt;a href="https://twitter.com/rdvornov"&gt;Roman&lt;/a&gt; just keeps pushing CSSTree to the next level, which is incredibly generous and exciting.&lt;/p&gt;

&lt;p&gt;Their enthusiasm and generosity really help driving Project Wallace forward and I can't wait to see what 2023 will bring us.&lt;/p&gt;

&lt;p&gt;Keep an eye on this little website!&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>datascience</category>
      <category>retrospective</category>
    </item>
    <item>
      <title>Things I learned giving CSS workshops</title>
      <dc:creator>Bart Veneman</dc:creator>
      <pubDate>Mon, 05 Dec 2022 15:33:55 +0000</pubDate>
      <link>https://forem.com/bartveneman/things-i-learned-giving-css-workshops-2bho</link>
      <guid>https://forem.com/bartveneman/things-i-learned-giving-css-workshops-2bho</guid>
      <description>&lt;p&gt;Being so frustrated at the capacity of modern day Frontend Devs to write 'proper' HTML and CSS, I decided to host a worskhop. For everyone interested, individually, minimal 3 hours each. These are the things I learned. About the devs, but also about myself and frontend in general.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Bring a design you like. A magazine, a dashboard, some conference site, whatever motivates you to build;&lt;/li&gt;
&lt;li&gt;We'll start writing some meaningful HTML as we walk through the design, no wrapper &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s allowed without purpose;&lt;/li&gt;
&lt;li&gt;After the HTML is (mostly) done, we start writing CSS;&lt;/li&gt;
&lt;li&gt;Work until satisfied or energy has run out.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The lows
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Devs writing &lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt; elements without &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; &lt;em&gt;and&lt;/em&gt; without &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; &lt;em&gt;(No description of the field, no place to send the data to…)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Devs wanting to write their own event handler to listen for &lt;code&gt;keydown&lt;/code&gt; on said input to check if &lt;code&gt;enter&lt;/code&gt; was pressed. &lt;em&gt;(Please use a regular &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; instead, because hitting enter in the input field or pressin enter/space while the submit button is focused will submit the form)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Apply &lt;code&gt;display: flex&lt;/code&gt; until it looks good&lt;em&gt;(Trust me: flexbox is great, but not the solution for all your problems)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Wrap a &lt;code&gt;div&lt;/code&gt; around pretty much anything because &lt;q&gt;it might come handy when starting to write the CSS&lt;/q&gt;&lt;em&gt;(No it doesn't and using a particular CSS framework put you in a mindset that you need to wrap every single element if you want to somehow align them. Even though the framework didn't advertise that at all.)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Nobody seems to have an idea about how &lt;code&gt;position: absolute&lt;/code&gt; works. Like it's some kind of magic box and tinkering with it will eventually solve their issue;&lt;/li&gt;
&lt;li&gt;Devs didn't recognise that their DevTools were telling them why their properties/values weren't working. DevTools do an excellent job these days of showing a warning when a declaration isn't applied because a parent element is missing a certain property, or some other reason. Clearly folks had never heard of this option;&lt;/li&gt;
&lt;li&gt;Some folks thought it's only possible to create rows and columns using &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s.&lt;em&gt;I don't even…&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The highs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;We got to spend roughtly ~50% on writing the HTML in workshop that was supposed to teach them about CSS and everyone loved it, no exceptions! Folks were generally unaware of all the options available in HTML. Also, they were very open to the idea of building the HTML first and then solving the styling with CSS, instead of trying to let their HTML match the design as closely as possible;&lt;/li&gt;
&lt;li&gt;Devs were really grateful for doing a walkthrough of these things together, as they felt it's so different from what they've always done but faster and with better results;&lt;/li&gt;
&lt;li&gt;Folks are pretty excited when they see what's prossible in modern CSS (with pretty good browser support): CSS Grid, Flexbox "magic", &lt;code&gt;accent-color&lt;/code&gt; and way more;&lt;/li&gt;
&lt;li&gt;Devs were pretty much aware that images need an &lt;code&gt;alt&lt;/code&gt; attribute 👍, but what to write in it is a mystery to them 👎;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frequently asked questions during the workshop
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;q&gt;How do I know when to use &lt;code&gt;px&lt;/code&gt;, &lt;code&gt;em&lt;/code&gt; or even something else?&lt;/q&gt; A &lt;a href="https://www.joshwcomeau.com/css/surprising-truth-about-pixels-and-accessibility/" rel="noopener noreferrer"&gt;recent article from Josh Comeau&lt;/a&gt; answers most of their questions: &lt;code&gt;rem&lt;/code&gt; in most cases, &lt;code&gt;em&lt;/code&gt; for some and hardly ever anything else (in our field, at least);&lt;/li&gt;
&lt;li&gt;
&lt;q&gt;How does CSS Grid even work? It looks way too magical to me!&lt;/q&gt; Usually I refer them to either &lt;a href="https://css-tricks.com/snippets/css/complete-guide-grid/" rel="noopener noreferrer"&gt;CSS Tricks&lt;/a&gt; or &lt;a href="https://cssgrid.io/" rel="noopener noreferrer"&gt;Wes Bos' excellent course&lt;/a&gt; on the topic;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>discuss</category>
    </item>
    <item>
      <title>How Project Wallace extracts all CSS from any webpage</title>
      <dc:creator>Bart Veneman</dc:creator>
      <pubDate>Thu, 01 Dec 2022 13:10:03 +0000</pubDate>
      <link>https://forem.com/bartveneman/how-project-wallace-extracts-all-css-from-any-webpage-18kp</link>
      <guid>https://forem.com/bartveneman/how-project-wallace-extracts-all-css-from-any-webpage-18kp</guid>
      <description>&lt;p&gt;&lt;strong&gt;TLDR; Getting all CSS from a webpage requires a couple of different methods and some filtering. Go straight to &lt;a href="/blog/extracting-css-from-webpage#the-algorithm"&gt;the summary&lt;/a&gt; or &lt;a href="https://github.com/projectwallace/extract-css-core"&gt;the GitHub repository&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Project Wallace would be nowhere without the prior art of CSS Stats. They came up with &lt;a href="https://github.com/cssstats/cssstats/tree/master/packages/get-css"&gt;get-css&lt;/a&gt; and this got me started in figuring out how to scrape CSS myself. Their algorithm is as simple as it is genius.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Take all &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tags from the page, go to it's &lt;code&gt;href&lt;/code&gt; and take the CSS&lt;/li&gt;
&lt;li&gt;Follow any &lt;code&gt;@import&lt;/code&gt; rule and take it's CSS&lt;/li&gt;
&lt;li&gt;Take all &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tags from the page and take it's CSS&lt;/li&gt;
&lt;li&gt;Follow any &lt;code&gt;@import&lt;/code&gt; rule and take it's CSS&lt;/li&gt;
&lt;li&gt;Combine all these chunks of CSS into a single piece.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When this approach doesn't work
&lt;/h3&gt;

&lt;p&gt;At the moment, they haven't included a way to scrape inline styles. I don't know whether that's intentional or not. For pages that utilize CSS-in-JS, the above method will not work. For that we need a browser that is able to evaluate styles at runtime, and looking at &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt;s and &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt;s is not enough. And with the rise of usage of CSS-in-JS, it's time for an improved version.&lt;/p&gt;

&lt;h2&gt;
  
  
  The in-depth way
&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;complicated&lt;/em&gt; way of getting all CSS involves a (optionally headless) browser and three key ingredients:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://developers.google.com/web/tools/chrome-devtools/coverage/"&gt;CSS Coverage&lt;/a&gt; &lt;a href="https://github.com/puppeteer/puppeteer/blob/master/docs/api.md#class-coverage"&gt;API&lt;/a&gt; (available in Puppeteer, so available for Firefox and Chromium-based browsers).&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/DocumentOrShadowRoot/styleSheets"&gt;HTML StyleSheets API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A plain old &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll"&gt;&lt;code&gt;document.querySelectorAll()&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The algorithm
&lt;/h3&gt;

&lt;p&gt;For the complete algorithm, check out &lt;a href="https://github.com/projectwallace/extract-css-core/blob/d365e7f699fe12c71640af8f6dfa572f86d2e14b/src/index.js"&gt;the source code GitHub&lt;/a&gt;. The short and readable version is something like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start the CSS Coverage reporter&lt;/li&gt;
&lt;li&gt;Go to the webpage&lt;/li&gt;
&lt;li&gt;Stop the CSS Coverage reporter&lt;/li&gt;
&lt;li&gt;Get all CSS-in-JS and &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tags with &lt;code&gt;document.styleSheets&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Get all inline styles with &lt;code&gt;document.querySelectorAll('[style]')&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Combine all chunks of CSS in a single chunk.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Coverage API
&lt;/h3&gt;

&lt;p&gt;The CSS Coverage API gives us all &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag CSS (and their &lt;code&gt;@import&lt;/code&gt;s). It also finds a lot of &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; CSS, but not the ones that were created with JavaScript, so we're ignoring those.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coverage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startCSSCoverage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;waitUntil&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;coverage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coverage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stopCSSCoverage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;links&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;coverage&lt;/span&gt;
  &lt;span class="c1"&gt;// Filter out the &amp;lt;style&amp;gt; tags that were found in the coverage&lt;/span&gt;
  &lt;span class="c1"&gt;// report since we've conducted our own search for them.&lt;/span&gt;
  &lt;span class="c1"&gt;// A coverage CSS item with the same url as the url of the page&lt;/span&gt;
  &lt;span class="c1"&gt;// we requested is an indication that this was a &amp;lt;style&amp;gt; tag&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;css&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;link-or-import&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/projectwallace/extract-css-core/blob/d365e7f699fe12c71640af8f6dfa572f86d2e14b/src/index.js#L88-L98"&gt;Source on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;document.styleSheets&lt;/code&gt; API
&lt;/h3&gt;

&lt;p&gt;With &lt;code&gt;document.styleSheets&lt;/code&gt; we have access to all &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tags that were server rendered, client-side rendered and all CSS that was generated with &lt;code&gt;StyleSheet.insertRule()&lt;/code&gt;, as used in many CSS-in-JS frameworks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Get all CSS generated with the CSSStyleSheet API&lt;/span&gt;
&lt;span class="c1"&gt;// This is primarily for CSS-in-JS solutions&lt;/span&gt;
&lt;span class="c1"&gt;// See: https://developer.mozilla.org/en-US/docs/Web/API/CSSRule/cssText&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;styleSheetsApiCss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;styleSheets&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="c1"&gt;// Only take the stylesheets without href, because those with href are&lt;/span&gt;
      &lt;span class="c1"&gt;// &amp;lt;link&amp;gt; tags, and we already tackled those with the Coverage API&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;stylesheet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;stylesheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;stylesheet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;stylesheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ownerNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tagName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
          &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;stylesheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;css&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;stylesheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cssRules&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;cssText&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;cssText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/projectwallace/extract-css-core/blob/afadea662233a4d865bc728cc0327c38d8aa1c63/src/index.js#L50-L65"&gt;Source on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Inline styles
&lt;/h3&gt;

&lt;p&gt;Now, this part may be a bit controversial, but I think it's worth to look at inline styles as well as all the rest. It's often overlooked, but many WordPress Themes, Magento plugins and other &lt;em&gt;Big Web Players©&lt;/em&gt; utilize inline styles for their themes and plugins. There's one catch, though. A CSS Rule consists of one or more selectors and zero or more declarations. The declarations are the ones present in the &lt;code&gt;style=""&lt;/code&gt;, but there is no selector. That's why I decided to give each individual block of inline styles it's own &lt;code&gt;[x-extract-css-inline-style]&lt;/code&gt; selector. This way, it's possible to &lt;em&gt;count&lt;/em&gt; the amount of inline style attributes after they were extracted from the page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Get all inline styles: &amp;lt;element style=""&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;// This creates a new CSSRule for every inline style&lt;/span&gt;
&lt;span class="c1"&gt;// attribute it encounters.&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="c1"&gt;// Example:&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="c1"&gt;// HTML:&lt;/span&gt;
&lt;span class="c1"&gt;//    &amp;lt;h1 style="color: red;"&amp;gt;Text&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="c1"&gt;// CSSRule:&lt;/span&gt;
&lt;span class="c1"&gt;//    [x-extract-css-inline-style] { color: red; }&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inlineCssRules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[style]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;style&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="c1"&gt;// Filter out empty style="" attributes&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inlineCss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;inlineCssRules&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`[x-extract-css-inline-style] { &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; }`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;inline&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/projectwallace/extract-css-core/blob/afadea662233a4d865bc728cc0327c38d8aa1c63/src/index.js#L67-L87"&gt;Source on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Bringing it all together
&lt;/h3&gt;

&lt;p&gt;The final step is to take the CSS of every step and merge that into one giant chunk of CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;links&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;styleSheetsApiCss&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inlineCss&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! A lot of work to get some CSS off of a page, but so far it's the most reliable way I've found to do it.&lt;/p&gt;

</description>
      <category>css</category>
      <category>javascript</category>
      <category>datascience</category>
      <category>webdev</category>
    </item>
    <item>
      <title>CSS complexity: it's complicated</title>
      <dc:creator>Bart Veneman</dc:creator>
      <pubDate>Thu, 17 Nov 2022 09:09:57 +0000</pubDate>
      <link>https://forem.com/bartveneman/css-complexity-its-complicated-2ba0</link>
      <guid>https://forem.com/bartveneman/css-complexity-its-complicated-2ba0</guid>
      <description>&lt;p&gt;Years ago I read a &lt;a href="https://csswizardry.com/2015/04/cyclomatic-complexity-logic-in-css/"&gt;blog post by Harry Roberts about Cyclomatic Complexity in CSS&lt;/a&gt;. You know, one of those classic Computer Science metrics that traditionally only classically trained engineers get to use. And for years this has me thinking that &lt;strong&gt;with CSS we also need a way to express cyclomatic complexity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There are many occasions where someone will audit the codebase and will want to know the overall state of the code, detect weak spots and find outliers. The proof of that in the many visitors on this very website that check their &lt;a href="https://www.projectwallace.com/analyze-css"&gt;CSS analytics&lt;/a&gt; or &lt;a href="https://www.projectwallace.com/css-code-quality"&gt;Code Quality&lt;/a&gt;. And to my knowledge there doesn't exist a tool that give you hints about the complexity of your CSS and I am convinced that we need this.&lt;/p&gt;

&lt;p&gt;And then came &lt;a href="https://www.bram.us/2022/06/28/the-css-cascade-a-deep-dive-2022-06-09-css-day/"&gt;Bramus at CSS Day 2022&lt;/a&gt;, who told us that most CSS architecture solutions like BEM and ITCSS are primarily aimed at &lt;em&gt;selectors&lt;/em&gt; and the struggle with the cascade. This has been stuck in my head ever since. It makes so much sense: selectors are usually the frustrating part of the &lt;em&gt;authoring&lt;/em&gt; experience (opposed to the &lt;em&gt;debugging&lt;/em&gt; experience). Once you have a specific enough selector, you keep adding declarations until it looks good.&lt;/p&gt;

&lt;h2&gt;
  
  
  Selector complexity beyond specificity
&lt;/h2&gt;

&lt;p&gt;Some recent additions to CSS have made it easier to deal with specificity issues, like &lt;code&gt;:where()&lt;/code&gt; and &lt;code&gt;:is()&lt;/code&gt;. Both give us the opportunity to write more complex selectors at the cost of little specificity added (or none, in the case of &lt;code&gt;:where()&lt;/code&gt;). But they can make selectors &lt;strong&gt;more complex&lt;/strong&gt;. Let's take a look at these two examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* specificity: 0,1,2 */&lt;/span&gt;
&lt;span class="nd"&gt;:is&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;footer&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* specificity: 0,1,2 */&lt;/span&gt;
&lt;span class="nt"&gt;header&lt;/span&gt; &lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://polypane.app/css-specificity-calculator/#selector=%3Ais(header%2C%20main%2C%20footer)%20p%3Ahover%2C%20header%20p%3Ahover"&gt;Specificity for both selectors is &lt;code&gt;0,1,2&lt;/code&gt;&lt;/a&gt;, but I'd argue that &lt;strong&gt;the first one is more complex&lt;/strong&gt; than the second, because of the arguments in &lt;code&gt;:is()&lt;/code&gt;. For me there's at least 6 parts to this selector, instead of the 3 (2 + 1) that the specificity would hint at. This is one example of specificity being not enough to explain the complexity of a selector.&lt;/p&gt;

&lt;h2&gt;
  
  
  More than just selector complexity
&lt;/h2&gt;

&lt;p&gt;Rarely do we talk about the 'hidden' complexity in CSS. The complexity that becomes visible when &lt;em&gt;debugging&lt;/em&gt;. This is where the order of things comes into play, as well as things like RuleSets being nested inside at-rules, like &lt;code&gt;@media&lt;/code&gt; or &lt;code&gt;@supports&lt;/code&gt; or soon &lt;code&gt;@container&lt;/code&gt;, but also vendor prefixes and &lt;a href="http://browserhacks.com/"&gt;browser hacks&lt;/a&gt; being used in selectors, properties, values, at-rule conditions. And you probably didn't author some of these, but they were added by Autoprefixer, PostCSS, Sass or ParcelCSS and now there's even more code to dig through! Even &lt;code&gt;@layer&lt;/code&gt; will not save us from all the nasty stuff that's tucked away in these rules, because it will only help with controlling the cascade, which in itself is another form of adding complexity.&lt;/p&gt;

&lt;p&gt;My goal is to work on a complete list and implement all these into Project Wallace's analyzer, so we can all inspect the things we've made. &lt;a href="https://github.com/projectwallace/css-analyzer/issues/218"&gt;Here's a list of things&lt;/a&gt; I'm considering to add to CSS complexity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Atrules

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@import&lt;/code&gt; can include media queries, supports conditions and a layer: &lt;code&gt;@import url(reset-mobile.css) supports(not (display: flex) and screen, (min-width: 1000px) layer(reset);&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@supports&lt;/code&gt; can contain vendor prefixes: &lt;code&gt;@supports (-webkit-appearance: none) {}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@keyframes&lt;/code&gt; can be vendor prefixed: &lt;code&gt;@-webkit-keyframes {}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@media&lt;/code&gt; can be a browserhack: &lt;code&gt;@media \\0 screen {}&lt;/code&gt;, &lt;code&gt;@media screen\9 {}&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Selectors

&lt;ul&gt;
&lt;li&gt;Attribute/value selectors: &lt;code&gt;[type]&lt;/code&gt;, &lt;code&gt;[type=text]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Vendor prefixes: &lt;code&gt;-moz-any(p)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Combinators: &lt;code&gt;.item ~ .sibling&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Forms: &lt;code&gt;input[type="tel"]:invalid:not(:focus):not(:placeholder-shown) + label&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Declarations:

&lt;ul&gt;
&lt;li&gt;Usage of &lt;code&gt;!important&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Properties:

&lt;ul&gt;
&lt;li&gt;Vendor prefixes: &lt;code&gt;-webkit-appearance&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Browserhacks: &lt;code&gt;*zoom&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Custom properties: &lt;code&gt;--brand-surface&lt;/code&gt; (I'm actually still undecided if this should count towards complexity, so I'd love to hear your thoughts. Again, &lt;a href="https://twitter.com/bramus/status/1363842359918800898"&gt;Bramus shows&lt;/a&gt; us there's definitely a complexity cost to pay in some circumstances and &lt;a href="https://www.youtube.com/watch?v=ZuZizqDF4q8"&gt;Lea Verou's talk&lt;/a&gt; also highlighted that custom properties can be really powerful, but that power comes with a complexity cost)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Values

&lt;ul&gt;
&lt;li&gt;Browserhacks: &lt;code&gt;10px !ie&lt;/code&gt;, &lt;code&gt;green \9&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Vendor prefixes: &lt;code&gt;-webkit-linear-gradient()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See? There are so many things! At first I thought it would be a little far fetched to start looking into these, but when I started working on selector complexity metrics a long time ago I noticed that many websites have plenty enough of any of these complexity issues going on. So I'm really looking forward to experimenting with this and what insights it will bring to us.&lt;/p&gt;

&lt;p&gt;On a closing note: I think Project Wallace is the most powerful tool to explore your CSS, but I also think that browser makers should step up their game here. Google Chrome had a nice start with their &lt;a href="https://dev.to/blog/css-analytics-in-chrome-devtools"&gt;CSS Overview&lt;/a&gt;, but I think the topic is still largely overlooked and developers are looking for ways to learn more about their CSS, beyond looking at the different colors and fonts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Special thanks to &lt;a href="https://www.bram.us/"&gt;Bramus van Damme&lt;/a&gt; for proof-reading this post!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>css</category>
      <category>datascience</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
