Giter Club home page Giter Club logo

lightningcss's Introduction

⚡️ Lightning CSS

An extremely fast CSS parser, transformer, and minifier written in Rust. Use it with Parcel, as a standalone library or CLI, or via a plugin with any other tool.

performance and build size charts

performance and build size charts

Features

  • Extremely fast – Parsing and minifying large files is completed in milliseconds, often with significantly smaller output than other tools. See benchmarks below.
  • Typed property values – many other CSS parsers treat property values as an untyped series of tokens. This means that each transformer that wants to do something with these values must interpret them itself, leading to duplicate work and inconsistencies. Lightning CSS parses all values using the grammar from the CSS specification, and exposes a specific value type for each property.
  • Browser-grade parser – Lightning CSS is built on the cssparser and selectors crates created by Mozilla and used by Firefox and Servo. These provide a solid general purpose CSS-parsing foundation on top of which Lightning CSS implements support for all specific CSS rules and properties.
  • Minification – One of the main purposes of Lightning CSS is to minify CSS to make it smaller. This includes many optimizations including:
    • Combining longhand properties into shorthands where possible.
    • Merging adjacent rules with the same selectors or declarations when it is safe to do so.
    • Combining CSS transforms into a single matrix or vice versa when smaller.
    • Removing vendor prefixes that are not needed, based on the provided browser targets.
    • Reducing calc() expressions where possible.
    • Converting colors to shorter hex notation where possible.
    • Minifying gradients.
    • Minifying CSS grid templates.
    • Normalizing property value order.
    • Removing default property sub-values which will be inferred by browsers.
    • Many micro-optimizations, e.g. converting to shorter units, removing unnecessary quotation marks, etc.
  • Vendor prefixing – Lightning CSS accepts a list of browser targets, and automatically adds (and removes) vendor prefixes.
  • Browserslist configuration – Lightning CSS supports opt-in browserslist configuration discovery to resolve browser targets and integrate with your existing tools and config setup.
  • Syntax lowering – Lightning CSS parses modern CSS syntax, and generates more compatible output where needed, based on browser targets.
    • CSS Nesting
    • Custom media queries (draft spec)
    • Logical properties
    • Color Level 5
      • color-mix() function
      • Relative color syntax, e.g. lab(from purple calc(l * .8) a b)
    • Color Level 4
      • lab(), lch(), oklab(), and oklch() colors
      • color() function supporting predefined color spaces such as display-p3 and xyz
      • Space separated components in rgb and hsl functions
      • Hex with alpha syntax
      • hwb() color syntax
      • Percent syntax for opacity
      • #rgba and #rrggbbaa hex colors
    • Selectors
      • :not with multiple arguments
      • :lang with multiple arguments
      • :dir
      • :is
    • Double position gradient stops (e.g. red 40% 80%)
    • clamp(), round(), rem(), and mod() math functions
    • Alignment shorthands (e.g. place-items)
    • Two-value overflow shorthand
    • Media query range syntax (e.g. @media (width <= 100px) or @media (100px < width < 500px))
    • Multi-value display property (e.g. inline flex)
    • system-ui font family fallbacks
  • CSS modules – Lightning CSS supports compiling a subset of CSS modules features.
    • Locally scoped class and id selectors
    • Locally scoped custom identifiers, e.g. @keyframes names, grid lines/areas, @counter-style names, etc.
    • Opt-in support for locally scoped CSS variables and other dashed identifiers.
    • :local() and :global() selectors
    • The composes property
  • Custom transforms – The Lightning CSS visitor API can be used to implement custom transform plugins.

Documentation

Lightning CSS can be used from Parcel, as a standalone library from JavaScript or Rust, using a standalone CLI, or wrapped as a plugin within any other tool. See the Lightning CSS website for documentation.

Benchmarks

performance and build size charts

performance and build size charts

$ node bench.js bootstrap-4.css
cssnano: 544.809ms
159636 bytes

esbuild: 17.199ms
160332 bytes

lightningcss: 4.16ms
143091 bytes


$ node bench.js animate.css
cssnano: 283.105ms
71723 bytes

esbuild: 11.858ms
72183 bytes

lightningcss: 1.973ms
23666 bytes


$ node bench.js tailwind.css
cssnano: 2.198s
1925626 bytes

esbuild: 107.668ms
1961642 bytes

lightningcss: 43.368ms
1824130 bytes

For more benchmarks comparing more tools and input, see here. Note that some of the tools shown perform unsafe optimizations that may change the behavior of the original CSS in favor of smaller file size. Lightning CSS does not do this – the output CSS should always behave identically to the input. Keep this in mind when comparing file sizes between tools.

lightningcss's People

Contributors

ahabhgk avatar arnaudbarre avatar chinedufn avatar cyjake avatar deckchairlabs avatar devongovett avatar georgeclaghorn avatar intrnl avatar joelmoss avatar joshstoik1 avatar kdy1 avatar kwonoj avatar leoniephiline avatar lucacasonato avatar lucasweng avatar marvinhagemeister avatar mayank99 avatar mischnic avatar nicksrandall avatar nicoburns avatar not-my-profile avatar onigoetz avatar oylenshpeegul avatar pfroud avatar robinmalfait avatar rrcobb avatar shinyaigeek avatar striezel avatar xeho91 avatar yisibl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

lightningcss's Issues

Minifier: Merging classes with the same name

I'm not sure if this is a bug or feature.

/*Input*/
.a { font-size: large; }
#b { color: aqua; }
.a { padding: 0.5rem; }
/*Output*/
.a{font-size:large}#b{color:#0ff}.a{padding:.5rem}

But if we have the same class adjacent to each other

/*Input*/
#b { color: aqua; }
.a { font-size: large; }
.a { padding: 0.5rem; }
/*Output*/
#b{color:#0ff}.a{padding:.5rem;font-size:large}

I would expect both inputs to produce the same output (the second one).

CSS variable substitution

I'm looking for a CSS parser that will replace CSS variables with their values (accounting for the cascade) for performance reasons.

I have CSS that looks like this throughout my stylesheets:

input.email-field-input {
  border: var(--email-input-border,
    var(--input-border,
      var(--border,
        1px solid red
      )
    )
  );
}

If in a theme file I define only one of these (for example):

:root {
  --input-border: 2px dotted blue;
}

Then I want to be able to run the parser and end up with:

input.email-field-input {
  border: 2px dotted blue;
}

Variables gone – correct values substituted.

But then if I define the same variable further down, say:

input {
  --input-border: 3px dashed yellow;
}

Then I would expect the compiled CSS to change accordingly:

input.email-field-input {
  border: 3px dashed yellow;
}

And, of course, if I defined none of the variables at all, I'd expect the final default:

input.email-field-input {
  border: 1px solid red;
}

Does Parcel-CSS do this? If not, is it or can it be under consideration as an enhancement?

I am still working out the kinks in this idea for theming a site, but however it works out, CSS variables are a performance hit I'd like to avoid. And unless they are dynamically generated, there is no point in not doing substitution in a static CSS file.

Thanks!

Please do not calculate the percentage in calc()

Original issue: evanw/esbuild#1821 (esbuild has been resolved).

.foo {
  width: calc(100% / 3);
}

lightningcss minifier output

.foo{width:33.3333%}

esbuild(old version) minifier output

.foo{width:33.33333%}

Any attempt to calculate and then simplify the percentages in calc() can be dangerous! Please turn off this feature by default.

Demo: https://codepen.io/yisi/pen/OJxVXYo

(Mac Chrome 89.0.4389.82)

Although a new version of Chrome switched to TableNG to fix this issue, there are still many other browsers that use older versions of Blink.

Please make sure that the CSS minifier does not break the page layout, in which case the number of compressed bytes is not that important.

See also: https://stackoverflow.com/questions/31719624/displaytable-div-with-percentage-width-1px-bug

Install

Not really an issue but i've been trying to figure out how to install this and the instruction aren't exactly clear

Minifier: Drop Redundant Doubled Properties?

Hi @devongovett ! 👋 First of all, thanks for all of your effort on this, really amazing for the community to have such a great tool!

I was looking for a feature of @parcel/css that would allow for removal of duplicate properties.

For example:

Input

div {
  color: green;
  color: red;
}

@parcel/css Output (Playground Link)

div{color:green;color:red}

Expected Output

div{color:red}

However, it could be a problem with my understanding of the CSS spec somehow, because I see that some duplicated properties do indeed get dropped:

Input

div {
  background: green;
  background: red;
}

@parcel/css Output (Playground Link)

div{background:red}

Support StyleSheet::new

I have a use case where I'd like to construct a new StyleSheet with an already populated CssRuleList. Is this something you would consider the libary to support?

unusedSymbols doesn't work with selectors like `.foo` or `#bar`

Unsure if this is how unusedSymbols is intended to work.

But say I have the following:

const { code } = css.transform({
    filename: 'style.css',
    code: Buffer.from(`
        .foo .bar {
            color: red;
        }

        .bar {
            color: green;
        }

        #foo {
            color: blue;
        }
    `),
    minify: true,
    unusedSymbols: ['#foo'],
})

The resulting code is:

.foo .bar{color:red}.bar{color:green}#foo{color:#00f}

Whereas

const { code } = css.transform({
    filename: 'style.css',
    code: Buffer.from(`
        .foo .bar {
            color: red;
        }

        .bar {
            color: green;
        }

        #foo {
            color: blue;
        }
    `),
    minify: true,
    unusedSymbols: ['foo'],
})

Produces:

.bar{color:green}

I would expect to be able to pass selectors to unusedSymbols as the typedocs mention as such https://github.com/parcel-bundler/parcel-css/blob/40868cbb111a13551a508629aa0d11c5e9204c64/node/index.d.ts#L30-L35

Development build not downleveling nested selectors?

I'm trying to replace sass with css + nested selectors. Everything looks fine in my production build, but the development build isn't providing valid styles to the browser. Looking at the browser's network tab, it seems the css that it's being served has nesting built into it. No browser currently supports nesting so I was expecting it to be transpiled.

parcel command:

parcel serve ./public/index.html --dist-dir .parcel-dev --public-url / --port 3000

.parcelrc:

{
  "extends": "@parcel/config-default",
  "transformers": {
    "*.css": ["@parcel/transformer-css-experimental"]
  },
  "optimizers": {
    "*.css": ["@parcel/optimizer-css"]
  }
}

@parcel/transformer-css config in package.json:

  "@parcel/transformer-css": {
    "drafts": {
      "nesting": true
    }
  }

browserslist config in package.json:

  "browserslist": {
    "production": [
      "last 3 chrome version",
      "last 2 firefox version",
      "last 1 safari version",
      "firefox esr"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }

Full project: (Link is to the PR making these changes to add css): nattthebear/ff12characterplanner#28

Missing dependency detect-libc

I was trying to add @parcel/css to https://github.com/GoalSmashers/css-minification-benchmark

running this package complains about a missing detect-libc package.

(On a side note, I added the package manually and @parcel/css is indeed the fastest in all cases, it does seem to trigger a ParseError { kind: Basic(EndOfInput), location: SourceLocation { line: 18, column: 79 } } on gumby.css though)

Looking forward for the future of this library, it's very promising !

[Optimization] Initial background

Before:

    .a {
        background: none
    }
    .b {
        background: none center
    }
    .c {
        background: url("test.png") transparent none repeat scroll
    }

After:

.a{background:#0000}.b{background:50% 50%}.c{background:url("test.png") transparent none repeat scroll}

For .a we even have deoptimization (none vs #0000).

Possible optimised output (from csso):

.a{background:0 0}.b{background:center}.c{background:url(test.png)}

Additional CLI features

As of #44 we have a basic CLI. Currently it can minify and output to stdout or a file, but might be nice to add a few more features.

  • --targets option. Could accept a browserslist and convert them to Parcel CSS targets with browserslist-rs (see #37).
  • --sourcemap option. Should output a source map file in addition to a css file, and add a sourceMappingUrl comment to the CSS.
  • --nesting option to enable parsing CSS nesting.
  • --css-modules option to enable CSS modules. Output a JSON file in addition to compiled CSS?
  • Ability to compile multiple files at once, either by passing file names or directories.

Downlevel logical properties

https://caniuse.com/css-logical-props

Only supported very recently. It can be down leveled, but might need to be opt-in because this changes the specificity of rules and requires a dir attribute to be placed on an ancestor element (since :dir(...) is not yet widely supported (https://caniuse.com/css-dir-pseudo).

.foo {
  margin-inline-start: 10px;
}

to

[dir="ltr"] .foo {
  margin-left: 10px;
}

[dir="rtl"] .foo {
  margin-right: 10px;
}

See https://github.com/csstools/postcss-logical and https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-dir-pseudo-class

[Optimization] Initial font

Before:

.a {
    font: normal normal 600 9px/normal Charcoal;
}
.b {
    font: normal normal 500 medium/normal Charcoal;
}
.c {
    font: normal normal 400 medium Charcoal;
}
.d {
    font: normal normal 500 medium/10px Charcoal;
}
.e {
    font: normal;
}
.f {
    font: bold;
}
.g {
    font: medium;
}

After:

.a{font:normal normal 600 9px/normal Charcoal}.b{font:normal normal 500 medium/normal Charcoal}.c{font:400px medium Charcoal}.d{font:normal normal 500 medium/10px Charcoal}.e{font:normal}.f{font:bold}.g{font:medium}

Possible optimised output (from csso):

.a{font:600 9px Charcoal}.b{font:500 Charcoal}.c{font:400 Charcoal}.d{font:500 medium/10px Charcoal}.e{font:normal}.f{font:700}.g{font:normal}

How to get CssRule as a selector string?

I'm starting to get my head around Rust, slightly. As a good little challenge and starter project I'm attempting to recreate https://github.com/FullHuman/purgecss using this library, find progress here https://github.com/deckchairlabs/purginator.

Is there an better way to get the Selector components into a string? I'm not sure how to do it correctly for the NestedRule

I'm currently doing the below:

https://github.com/deckchairlabs/purginator/blob/83a246d658e74bd02b5309ee6804d4f5ecdd6ed5/purginator/src/purger.rs#L8-L17

Some logical properties are supported but `inset` is not

I was having a look at what postcss plugins can be replaced by @parcel/css

Since the documentation mentions that logical properties are supported, I tried it and got an incomplete result:

Input:

.banner {
    color: #222222;
    inset: logical 0 5px 10px;
    padding-inline: 20px 40px;
    resize: block;
    transition: color 200ms;
  }

Output according to https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-logical

.banner:dir(ltr) {
  padding-left: 20px;
  padding-right: 40px;
}
.banner:dir(rtl) {
  padding-right: 20px;
  padding-left: 40px;
}
.banner {
  color: #222222;
  top: 0;
  left: 5px;
  bottom: 10px;
  right: 5px;
  resize: vertical;
  transition: color 200ms;
}

Output of @parcel/css, with edge 18 as target browser

.banner {
  color: #222;
  inset: logical 0 5px 10px;
  resize: block;
  padding-left: var(--ltr, 20px) var(--rtl, 40px);
  padding-right: var(--ltr, 40px) var(--rtl, 20px);
  transition: color .2s;
}

[dir="ltr"] {
  --ltr: initial;
  --rtl: ;
}

[dir="rtl"] {
  --ltr: ;
  --rtl: initial;
}

I do get the trick with custom properties but inset should be converted into top left, bottom and right

Here's a link to the proposal : https://drafts.csswg.org/css-logical/#propdef-inset

Usually I'd propose to make a PR but I currently have no knowledge in Rust, I'll learn but that might take time.

`border` and `border-width` interact to produce expanded code

The snippet .foo { border: 1px solid black; border-width: 1px 1px 0 0; } gets transpiled+minified to .foo{border-top:1px solid #000;border-bottom:0 solid #000;border-left:0 solid #000;border-right:1px solid #000}.

const css = require('@parcel/css');

const a = `.foo { border: 1px solid black; border-width: 1px 1px 0 0; }`
let { code } = css.transform({
  filename: 'style.css',
  code: Buffer.from(a),
  minify: true,
  sourceMap: true,
  targets: {
    // Semver versions are represented using a single 24-bit number, with one component per byte.
    // e.g. to represent 13.2.0, the following could be used.
    safari: (13 << 16) | (2 << 8)
  }
});

console.log(`${a} transformed to ${code}`);

Outputs: .foo { border: 1px solid black; border-width: 1px 1px 0 0; } transformed to .foo{border-top:1px solid #000;border-bottom:0 solid #000;border-left:0 solid #000;border-right:1px solid #000}

error with star-prefixed css properties

const css = require('@parcel/css');

css.transform({
    filename: 'style.css',
    code: Buffer.from('.clearfix { *zoom: 1; }'),
    minify: true
});

results in

SyntaxError: Unexpected token Delim('*')
    at Object.<anonymous> (/home/dominikg/develop/vite-ecosystem-ci/parcel.cjs:3:5)
    at Module._compile (node:internal/modules/cjs/loader:1101:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:17:47 {
  fileName: 'style.css',
  source: '.clearfix { *zoom: 1; }',
  loc: { line: 1, column: 13 }
}

found via css-minification-benchmark. GoalSmashers/css-minification-benchmark#101

Some of their test data is old and still has them: https://github.com/GoalSmashers/css-minification-benchmark/blob/e1d52eaea8e1107e95ea06f5cc085227cd36b2c0/data/gumby.css#L1216

iirc they were used to add special treatment for ancient IE

https://stackoverflow.com/questions/1667531/what-does-a-star-preceded-property-mean-in-css

Optimization idea: Split file by media query(s).

Not sure if it makes sense to live in this package but since we have the AST, would be easy to implement here.

The basic idea is that we could take a css file like this as input:

/* Set the background color of body to tan */
body {
    background-color: tan;
}

/* On screens that are 992px or less, set the background color to blue */
@media screen and (max-width: 992px) {
    body {
        background-color: blue;
    }
}

/* On screens that are 600px or less, set the background color to olive */
@media screen and (max-width: 600px) {
    body {
        background-color: olive;
    }
}

/* Show dark mode when user prefers it */
@media (prefers-color-scheme: dark) {
  body  { 
    background:  #333; 
    color: white;
  }
}

And then split it into 4 files, one for each unique media query included in the file:
Media: N/A

body {
    background-color: tan;
}

Media: screen and (max-width: 992px)

body {
    background-color: blue;
}

Media: screen and (max-width: 600px)

body {
    background-color: olive;
}

Media: (prefers-color-scheme: dark)

body  { 
  background:  #333; 
  color: white;
}

This list of split files could then be used by bundlers (like parcel) to generate multiple <link rel="stylesheet" media="..." href="..." /> in the HTML.

This would be a performance optimization as link tags with media queries are non-render blocking for the browser.

Different values for vendor-prefixed shorthand properties causes decomposition into individual properties

.foo {
  -webkit-transition: background 200ms;
  -moz-transition: background 200ms;
  transition: background 230ms;
}

is transformed into

.foo {
  transition-property: background;
  -webkit-transition-duration: .2s;
  -moz-transition-duration: .2s;
  -webkit-transition-delay: 0s;
  -moz-transition-delay: 0s;
  -webkit-transition-timing-function: ease;
  -moz-transition-timing-function: ease;
  transition-duration: .23s;
  transition-delay: 0s;
  transition-timing-function: ease;
}

I would expect to keep the shorthand properties.

CSS playground

Please note that the vendor-prefixed properties being different is unlikely to be the author's intent in most cases and the tool could produce a warning.

napi link error on Windows when using crate parcel_css

Hi there,

I'm trying to use the crate parcel_css for a rust project on Windows 11 but face a link error related to napi.

I can reproduce the issue with an empty project importing parcel_css.

# File Cargo.toml
[package]
name = "plop"
version = "0.1.0"
edition = "2021"

[dependencies]
parcel_css = "1.0.0-alpha.13"
// File src/main.rs
use parcel_css::stylesheet::StyleSheet;

fn main() {
    let _style = StyleSheet::parse(
        "plop.css".to_owned(),
        "#hello { color: blue; }",
        Default::default(),
    )
    .unwrap();
}
$ cargo clean
$ cargo build
 # ...lot of stuff...
  = note: libnapi-41e4dc39fadfbdc1.rlib(napi-41e4dc39fadfbdc1.napi.6ae83a60-cgu.2.rcgu.o) : error LNK2019: unresolved external symbol napi_get_undefined referenced in function _ZN4napi3env3Env13get_undefined17hb7a6765f73f38a65E
 # ... more stuff...
  fatal error LNK1120: 11 unresolved externals

That being said, I looked a bit into ways to fix the issue and found out my builds would link correctly if I add a build script build.rs to my project (the one importing parcel_css):

# File Cargo.toml
# ...

# Add this build dep 👇
[build-dependencies]
napi-build = "1"
// File build.rs
extern crate napi_build;

fn main() {
    napi_build::setup()
}
$ cargo clean
$ cargo build
   Compiling plop v0.1.0 (C:\Users\Sam\Development\Rust\plop)
warning: cargo:rustc-cdylib-link-arg was specified in the build script of plop v0.1.0 (C:\Users\Sam\Development\Rust\plop), but that package does not contain a cdylib target

Allowing this was an unintended change in the 1.50 release, and may become an error in the future. For more information, see <https://github.com/rust-lang/cargo/issues/9562>.
warning: cargo:rustc-cdylib-link-arg was specified in the build script of plop v0.1.0 (C:\Users\Sam\Development\Rust\plop), but that package does not contain a cdylib target

Allowing this was an unintended change in the 1.50 release, and may become an error in the future. For more information, see <https://github.com/rust-lang/cargo/issues/9562>.
    Finished dev [unoptimized + debuginfo] target(s) in 2.02s

(the build yields warnings, but is successful)


I don't really get why napi is required when using the crate parcel_css, it seems to be a dependency of parcel_css_node but not the other crates.

A few thoughts:

  • Should a build.rs script be added to parcel_css to be sure napi is always built?
  • Could the issue be that the cargo workspace is declared in the same Cargo.toml as parcel_css?
  • It might be a good idea to also run tests on a windows host (via github action).

What do you think?

CSS Color Level 4

https://www.w3.org/TR/css-color-4/

  • lab
  • lch
  • oklab
  • oklch
  • color()

Only supported by Safari currently. We can downlevel by converting to RGB, but we should preserve the original as well because it will be displayed with a wider color gamut than RGB so we can't perfectly emulate it.

Deno support?

Are there any plans to support Deno? I've tried to import it, but get errors about missing ./pkg directory.

When importing from Skypack https://cdn.skypack.dev/@parcel/css...

@parcel/css/native.js
   Import "./pkg" could not be resolved from file.

Feature request: support custom media queries

The Media Queries level 5 working draft supports "custom media queries". This is one of the features required to use open-props in a Rust application, but would be useful overall. Prior art exists in the PostCSS Custom Media plugin. It'd be great if parcel-css could support this too. Thanks!

Example

input

/* --modern targets modern devices that support color or hover */
@custom-media --modern (color), (hover);

@media (--modern) and (width > 1024px) {
  .a { color: green; }
}

output

@media ((color) or (hover)) and (width > 1024px) {
  .a { color: green; }
}

References

Downlevel :is

The :is selector is fairly new so we should probably downlevel it: https://caniuse.com/css-matches-pseudo. We use it currently to compile nesting as well.

In the general case, this is hard if not impossible to do correctly. It can easily explode in combinations and generate massive CSS, and in some cases might be challenging without knowing the DOM because it's essentially boolean logic (if this selector matches, AND this selector matches). But may work in simple cases. See also postcss/postcss-selector-matches#19 (comment) and https://github.com/csstools/postcss-is-pseudo-class.

Perhaps we could compile to the older :matches or :-webkit-any syntax at least.

Comments are removed even with `minify: false`

I have seen that any input with comment is output without comments even when minification is disabled :

const css = require("@parcel/css");

const inputs = [
  "/* before */ rule { c: 1 } /*! after */",
  `/* before */
            rule { d: 1 }
            /*! after */`
];

inputs.forEach((input) => {
  const { code } = css.transform({
    filename: "",
    code: Buffer.from(input),
    minify: false
  });

  console.log({
    input,
    output: code.toString()
  });
});

Results in

{
  input: '/* before */ rule { c: 1 } /*! after */',
  output: 'rule {\n  c: 1;\n}\n'
}
{
  input: '/* before */\n            rule { d: 1 }\n            /*! after */',
  output: 'rule {\n  d: 1;\n}\n'
}

For most comments that shouldn't be a problem, but if the comment starts with /*! in general it should be preserved as it is used for licenses or other content that was specifically marked to remain in the code.

Is there a way to keep those comments ?

css`inset` is not support for Safari, but it still convert to `inset` without fallback

cssinset is not support for Safari, but it still convert to inset without fallback

=== package.json ===

"parcel": "^2.2.0",
"browserslist": [
  "last 2 versions",
  "> 1%",
  "safari >= 11",
  "ie >= 10"
],

=== .parcelrc ===
{
"extends": "@parcel/config-default",
"transformers": {
".html": ["...", "parcel-transformer-html-datasrc"],
"
.css": ["@parcel/transformer-css-experimental"]
},
"optimizers": {
"*.css": ["@parcel/optimizer-css"]
},
}

=== source code ===

.foo {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
}

=== after parcel build ===
.foo{-ms-flex-pack:center;justify-content:center;-ms-flex-align:center;align-items:center;display:-ms-flexbox;display:flex;position:absolute;inset:0}

=== expect result ===
.foo{-ms-flex-pack:center;justify-content:center;-ms-flex-align:center;align-items:center;display:-ms-flexbox;display:flex;position:absolute;inset:0;top:0;right:0;bottom:0;left:0}

Mozilla Public License 2.0 compliance issue

Hi, Parcel-CSS team. My name is Mike Hoye, and I work for Mozilla.

We've noticed that you've re-used a substantial amount of the Firefox style system code in parcel-css; for example, compare

While that's OK and we're always happy to see Mozilla code finding new life in new projects, we license it under the Mozilla Public License, rather than the MIT license parcel-css is using, and modified MPL-licensed code should remain covered by the MPL. I'm sure this was an unintentional oversight, but we'd appreciate it if you could make the necessary corrections in those files relying on Mozilla-created code..

Thank you, and you can reach me at mhoye at mozilla dot com if you have any questions.

  • mhoye

Improve installation size

Currently @parcel/css includes .node files for all platforms regardless of target platform, which results in ~32mb disk space usage.

Is there any reason for not selectively installing the platform-specific binary (via optional dependencies) like esbuild does? That could bring down the download size significantly to ~4mb (although results in more resolution http calls).

Source maps

Should use the parcel_sourcemap crate from https://github.com/parcel-bundler/source-map.

I think it would be fine to just map the rules, and not individual declarations/values. That's all browser dev tools show anyway. Should be able to get location information from the parser already, store this in the rule nodes, and generate a source map from that in the printer.

Suggestion: Consider switching to "browserslist-rs"

There's already browserslist-rs, so we can consider switching to it.

Pros:

  • It's written in Rust and "parcel-css" is also written in Rust, so integration can be easier.
  • It doesn't rely on JavaScript, so if someone uses "parcel-css" as a Rust crate, he/she can also use "browserslist" feature. Also, online demo can also support this.

Cons:

  • So far, "browserslist-rs" has implemented most of the features in JavaScript-based "browserslist", but not 100%, so some rarely used features can't be available.
  • "Can I Use" data are bundled, while in JavaScript-based "browserslist" those data are installed as dependencies, so data statistics may not up-to-date.

Nesting transform is incorrect?

First, thank you for making this library. It's wonderful to see something like this, especially as an independent library, and I know how much work it is to make one of these.

I looked into transforming CSS nesting in esbuild recently and I was wondering how this library does it. I think I've found a case where this implementation deviates from the CSS nesting specification:

.foo .bar {
  &div { color: red }
}

This library transforms that into this code (live link):

div.foo .bar{color:red}

However, if I'm reading it correctly the specification says this should be transformed into the following code instead (in this section):

div:is(.foo .bar){color:red}

These two code snippets are not equivalent. For example, the following HTML is supposed to render as black if the specification is followed, but renders as red with the output from this library:

<div class="foo">
  <span class="bar">stuff</span>
</div>

Just letting you know about this edge case. I don't need this edge case to work since I'm not using your library myself. Feel free to do what you want with this issue.

Potentially related: #19

Variable name minification

I'm not sure if this is inline with project goals but I've noticed modern CSS has a lot of very long variable names. If variables are declared in the same file it is somewhat safe to rename the variable names to something shorter.

It's worth noting that shorter variable names are often not that useful for assets that are gzipped because Gzip does this optimization.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.