Skip to content

@layer ordering declaration is silently dropped when followed by @layer blocks #1222

@madogiwa0124

Description

@madogiwa0124

Description

When a CSS file contains a standalone @layer ordering declaration followed by @layer blocks for the same layer names, transform() (and bundle()) silently drops the ordering declaration from the output — regardless of the minify option.

Environment

  • lightningcss: 1.32.0
  • Reproduced on:
    • macOS 26.3.1, arm64, Node.js v24.13.0
    • Linux (Alpine), x86_64, Node.js v24.15.0
    • Linux (Alpine), arm64, Node.js v24.15.0

Reproduction

// repro.mjs
import { transform } from "lightningcss";

const input = `
@layer base, layout, components, utils;

@layer base { body { margin: 0; } }
@layer layout { .container { max-width: 1200px; } }
@layer components { .btn { padding: 8px 16px; } }
@layer utils { .hidden { display: none; } }
`.trim();

for (const minify of [false, true]) {
  const { code } = transform({
    filename: "test.css",
    code: Buffer.from(input),
    minify,
  });
  const output = code.toString();
  const needle = minify
    ? "@layer base,layout,components,utils;"
    : "@layer base, layout, components, utils;";
  console.log(
    `minify: ${minify} → ordering preserved: ${output.includes(needle) ? "✓ OK" : "✗ BUG"}`,
  );
  console.log(output);
  console.log("---");
}
$ node repro.mjs
minify: false → ordering preserved: ✗ BUG
@layer base {
  body {
    margin: 0;
  }
}

@layer layout {
  .container {
    max-width: 1200px;
  }
}

@layer components {
  .btn {
    padding: 8px 16px;
  }
}

@layer utils {
  .hidden {
    display: none;
  }
}

---
minify: true → ordering preserved: ✗ BUG
@layer base{body{margin:0}}@layer layout{.container{max-width:1200px}}@layer components{.btn{padding:8px 16px}}@layer utils{.hidden{display:none}}
---

Expected

The ordering declaration @layer base, layout, components, utils; should be preserved in the output
in both minify: false and minify: true modes.

Actual

The ordering declaration is completely absent from the output. The @layer blocks themselves are preserved, so the layers exist but their explicit ordering is gone.

Additional findings

Testing two variations narrows down the trigger:

Input minify: false minify: true
ordering declaration only (no blocks) ✓ preserved ✓ preserved
ordering declaration + blocks (original repro) ✗ dropped ✗ dropped

The drop only occurs when the same layer names appear in both the ordering declaration and
subsequent @layer blocks. When the ordering declaration appears alone (no blocks), it is
preserved as expected.

bundle() is also affected:

import { bundle } from "lightningcss";

// with the same input written to a file:
bundle({ filename: "test.css", minify: false });
// → ordering declaration is also dropped

Notes

Per MDN:

"the initial order in which layers are declared indicates which layer has precedence"

A standalone @layer ordering declaration is explicitly described as a mechanism to establish
layer precedence order. This indicates it carries semantic meaning and should be preserved in the output.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions