parcel-bundler / parcel-bundler/lightningcss

Further optimize `@layer` minification opportunities

Open
#423 6 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

area: minification
Dominant language
Rust
Stars
7.7k
Forks
302
PR merge metrics
No merged PRs in 30d

Description

Cascade layers support a few things that Lightning CSS doesn't currently take into consideration:

  • Nested layers both by nesting directly as well as using dot notation to insert into a nested layer
  • Anonymous layers, which are treated as unique and can't be combined
  • Predefined layer order using @layer without a block

That means input like this:

@layer one, two, three;

@layer three.utilities {
  .bg-black {
    background: #000;
  }
}

@layer two {
  body {
    background: red;
  }
  @layer base {
    body {
      font-size: 10px;
    }
  }
}

@layer {
  .class-1 {
    color: blue;
  }
}

@layer three {
  @layer utilities {
    .mt-10 {
      margin-top: 10px;
    }
  }
  html {
    color: white;
  }
}

@layer two.base {
  h1 {
    font-size: 24px;
  }
}

@layer two {
  h2 {
    font-size: 20px;
  }
}

@layer one {
  p {
    font-size: 16px;
  }
}

@layer {
  .class-2 {
    color: red;
  }
}

...can be minified to this sort of shape:

@layer one {
  p {
    font-size: 16px;
  }
}

@layer two {
  body {
    background: red;
  }
  h2 {
    font-size: 20px;
  }
  @layer base {
    body {
      font-size: 10px;
    }
    h1 {
      font-size: 24px;
    }
  }
}

@layer three {
  @layer utilities {
    .bg-black {
      background: #000;
    }
    .mt-10 {
      margin-top: 10px;
    }
  }
  html {
    color: white;
  }
}

@layer {
  .class-1 {
    color: blue;
  }
}

@layer {
  .class-2 {
    color: red;
  }
}

This is unfortunately complicated, hah 😄 It would be pretty amazing to be able to handle this sort of thing at the minification layer as I have to imagine it makes the browser's job easier.

I ask selfishly because I'm anticipating a future for Tailwind CSS where we leverage cascade layers to avoid a bunch of the complex sorting logic that exists in the framework right now, and instead generate code that looks like this:

@layer defaults, base, components, utilities, variants;

@layer base {
  html { /* ... */ }
  /* ... */
}

@layer components {
  .container { /* ... */ }
}

.shadow-sm {
  @layer defaults {
    --tw-ring-offset-shadow: 0 0 #0000;
    --tw-ring-shadow: 0 0 #0000;
    --tw-shadow: 0 0 #0000;
    --tw-shadow-colored: 0 0 #0000
  }
  @layer utilities {
    --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
    --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);
    box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)
  }
}

.blur {
  @layer defaults {
    --tw-blur:  ;
    --tw-brightness:  ;
    --tw-contrast:  ;
    --tw-grayscale:  ;
    --tw-hue-rotate:  ;
    --tw-invert:  ;
    --tw-saturate:  ;
    --tw-sepia:  ;
    --tw-drop-shadow:  
  }
  @layer utilities {
    --tw-blur: blur(8px);
    filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)
  }
}

.hover\:scale-125 {
  @layer defaults {
    --tw-translate-x: 0;
    --tw-translate-y: 0;
    --tw-rotate: 0;
    --tw-skew-x: 0;
    --tw-skew-y: 0;
    --tw-scale-x: 1;
    --tw-scale-y: 1
  }
  @layer variants {
    &:hover {
      --tw-scale-x: 1.25;
      --tw-scale-y: 1.25;
      transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))
    }
  }
}

...and delegate all of the work for processing nesting and minification to Lightning CSS (or our own separate production-ifying layer if needed, not trying to make demands here of course!) to get output more like this:

@layer defaults {
  .shadow-sm {
    --tw-ring-offset-shadow: 0 0 #0000;
    --tw-ring-shadow: 0 0 #0000;
    --tw-shadow: 0 0 #0000;
    --tw-shadow-colored: 0 0 #0000;
  }
  .blur {
    --tw-blur: ;
    --tw-brightness: ;
    --tw-contrast: ;
    --tw-grayscale: ;
    --tw-hue-rotate: ;
    --tw-invert: ;
    --tw-saturate: ;
    --tw-sepia: ;
    --tw-drop-shadow: ;
  }
  .hover\:scale-125 {
    --tw-translate-x: 0;
    --tw-translate-y: 0;
    --tw-rotate: 0;
    --tw-skew-x: 0;
    --tw-skew-y: 0;
    --tw-scale-x: 1;
    --tw-scale-y: 1;
  }
}

@layer base {
  html { /* ... */ }
  /* ... */
}

@layer components {
  .container { /* ... */ }
}

@layer utilities {
  .shadow-sm {
    --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
    --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);
    box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
      var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
  }
  .blur {
    --tw-blur: blur(8px);
    filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast)
      var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert)
      var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
  }
}

@layer variants {
  .hover\:scale-125:hover {
    --tw-scale-x: 1.25;
    --tw-scale-y: 1.25;
    transform: translate(var(--tw-translate-x), var(--tw-translate-y))
      rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y))
      scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
  }
}

Thought it was worth raising just to bring some awareness at least to all of these extra cascade layer features that might need to be considered.

This is a really fantastic post that covers all of this stuff in quite a bit of depth:

https://css-tricks.com/css-cascade-layers/

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

The issue names no source files, tests, or entry points. Begin by tracing Lightning CSS's cascade-layer minification and assessing the nested, anonymous, and predeclared-layer cases described here. Done would mean safely combining and ordering eligible layers while preserving the distinct behavior of anonymous layers, with tests covering these examples.

Written by the indexing model from the issue text.

Assessment

Tech stack
css, rust
Domain
compilers, performance
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.