Skip to content

Releases: parcel-bundler/lightningcss

v1.9.0

25 May 04:00
Compare
Choose a tag to compare

This release includes new features for CSS modules, support for compiling the system-ui font family, and a few other bug fixes and improvements.

Locally scoped CSS variables

Just like other identifiers such as class names, ids, and @keyframes, Parcel CSS now supports locally scoping CSS dashed identifiers such as variable names and @font-palette-values names. This prevents variables in one file from clashing with variables in another. By default, all variable references via the var() function are scoped to the module, so you can only reference variables declared in that file. To reference a variable declared in a different CSS module, you can use a new syntax extension:

.button {
  background: var(--accent-color from "./colors.module.css");
}

You can also reference a global variable like this:

.button {
  color: var(--color from global);
}

CSS variables may also be referenced from JavaScript, the same way as with classes:

import * as style from './style.module.css';
style['--foo']

This applies not only to CSS variables but anywhere the <dashed-ident> syntax is used in CSS. Right now that means @font-palette-values and @property, but others such as @custom-media and @color-profile are coming.

All of this is opt-in with the dashedIdents option of the cssModules config object.

{
  "cssModules": {
    "dashedIndents": true
  }
}

Custom CSS modules naming patterns

You can now configure how Parcel CSS generates names in CSS modules, via the pattern option of the cssModules config object.

{
  "cssModules": {
    "pattern": "library-[name]-[hash]-[local]"
  }
}

The currently supported segments are:

  • [name] - the base name of the CSS file, without the extension
  • [hash] - a hash of the full file path
  • [local] - the original class name

Support for compiling system-ui font

The system-ui font family will now be compiled for older browsers to include a font stack that works across most common operating systems.

Input:

.foo {
  font-family: system-ui;
}

Output:

.foo {
  font-family: system-ui, AppleSystem, BlinkMacSystemFont, Segoe UI, Roboto, Noto Sans, Ubuntu, Cantarell, Helvetica Neue;
}

Thanks to @onigoetz for contributing this feature!

Other fixes and improvements

  • Support for custom resolve() method in SourceProvider trait for Rust bundler API (contributed by @dgp1130) - #177
  • Allow fragment URLs in custom properties - f6f1673
  • Bump Rust toolchain to v1.61.0, and drop deprecated retain_mut dependency (contributed by @joshstoik1) - #184

v1.8.3

12 May 19:00
Compare
Choose a tag to compare

This release includes enhancements to the Parcel CSS Rust API, and some bug fixes.

New Rust API features

CSSOM

This release begins to implement the CSS Object Model (CSSOM) API. For now, that includes some new methods in the Rust API to get, set, and remove properties. This supports reading and writing both shorthand (e.g. border) and longhand (e.g. border-left-width) properties, no matter which properties were originally written in the rule. The Property and PropertyId enums have also been updated to expose some metadata about shorthand and longhand properties as well. Check out the docs for more details.

Source locations

Also in this release is a new way to lazily compute the original line and column ranges for a CSS property. This information is not stored during parsing because would use a lot of memory, and it is rarely used except in error scenarios. However, it can be recomputed on demand when needed. This can be done using the property_location method of a StyleRule. You must provide the original source string that was used to parse the stylesheet along with the index of the property to retrieve the location for.

Serde support

Finally, we added Serde support for all nodes in the AST, so you can serialize or deserialize from JSON and other formats. Given Parcel CSS has such a detailed parsing of each CSS rule, property, and value, this may be useful for applications written in other languages that want to manipulate parsed CSS values. This is enabled behind the serde feature flag. Check out the example of how to serialize a stylesheet for more details.

Fixes

  • Fix compat data for margin-inline and padding-inline shorthand properties – 0d7e4c6
  • Parse z-index property and fix rounding issue with serialization – 839bcd1

v1.8.2

30 Apr 18:01
Compare
Choose a tag to compare

This release includes bug fixes.

  • Always output quoted url() when not minifying. This fixes a bug when using data URLs in Parcel. #160
  • Mark jemallocator as a CLI-only dependency in the Rust crate. #157
  • Upgrade to napi-rs v2. #161
  • Improve whitespace printing when non-minified. 677277c
  • Fix stack overflow while simplifying calc expressions with nested functions 9a64787
  • Fix minification of border: none shorthand 1f66776
  • Fix interpretation of "not all" in media queries f146b66

v1.8.1

11 Apr 15:10
Compare
Choose a tag to compare

Fixed

  • Compile text-decoration-thickness percentage to a calc for non-supporting browsers.
  • Don't include text-decoration-thickness in text-decoration shorthand for non-supporting browsers.
  • Do not convert out of bounds rotation transforms to a matrix, or wrap around 360 degrees.
  • Only output known prefixes for the :is selector
  • Manually construct napi buffers to avoid a copy and workaround a Node.js crash with zero-length vectors.

Playground

v1.8.0

07 Apr 16:01
Compare
Choose a tag to compare

This release adds support for compiling several selector features for older browsers, improves the Rust API, and fixes some bugs.

Features

  • Downlevel :dir selector for non-supporting browsers. This is compiled to the :lang selector, which is the closest equivalent. However, it is not perfect. :dir is meant to be affected by both the dir HTML attribute and the direction CSS property, but :lang is only affected by the lang HTML attribute. When setting the dir HTML attribute the lang is usually also set, so it should work for most cases, but it is not possible to polyfill 100% correctly in pure CSS, so keep this in mind.
  • Support for :lang selector with multiple arguments, e.g. :lang(en, fr, de) This is currently only supported natively in Safari. Parcel CSS will generate fallbacks for other browsers using the :is selector, e.g. :is(:lang(en), :lang(fr), :lang(de)).
  • Downlevel :is selector in simple cases. The :is selector is fairly recent, but older browsers have supported the :-webkit-any and :-moz-any selectors, which are equivalent but do not support complex selectors (e.g. selectors with combinators) as arguments. Parcel CSS will compile :is to these fallbacks depending on your browser targets when only simple selectors are used.
  • A new approach to compiling CSS Logical Properties. Logical properties such as border-start-start-radius allow you to define many CSS properties in terms of the writing direction, so that UIs mirror in right-to-left languages. Parcel CSS used to compile logical properties using CSS variables, but this had some problems with the cascade when overriding values defined in other rules. Now, we use the :dir or :lang selectors (as described above) to compile these properties instead.
  • Rust API improvements. The Parse and ToCss traits are now exposed, which allows you to parse and serialize individual CSS rules and values. See #140 for an example. Errors also now implement std::error::Error.

Fixes

v1.7.4

31 Mar 14:41
Compare
Choose a tag to compare

This release includes bug fixes.

  • Allow empty string in @-moz-document url-prefix() function
  • font-family: revert/revert-layer cannot remove quotation marks
  • Reserve revert-layer keyword in custom ident
  • Support pseudo classes on ::-webkit-scrollbar pseudo elements
  • Parse predefined list styles as enum so they aren't renamed in css modules
  • Ensure custom property names are escaped
  • Fix css module hashing with implicit CSS grid line names

v1.7.0

19 Mar 23:30
Compare
Choose a tag to compare

This release adds a number of new features, including improved vendor prefixing support, and support for new CSS syntax features.

  • Vendor prefixing and improved minification for mask properties. This includes collapsing multiple separate properties into shorthands, as well as support for new color fallbacks. Example.
  • Vendor prefixing for clip-path
  • Vendor prefixing for filter
  • Downlevel :not selector list syntax for older browsers
  • Parsing support for :has, now supported natively in Safari.
  • Support for @font-palette-values rule and font-palette property. These let you override the color palette of color fonts, such as emoji.
  • Support for many more length units, including in calc() simplification. These include new viewport units, and new font relative units from css-values-4.
  • Analyze url() dependencies in custom properties. Only absolute URLs are supported. Relative paths will throw an error. This is because in the browser, urls in custom properties resolve from the location where the var() is used, not where the custom property is defined. Therefore, without actually applying all var() usages, it's not possible to know all of the potential urls that will be resolved. Enforcing that these urls must be absolute resolves this ambiguity.
  • Update compatibility data from caniuse and mdn

v1.6.0

10 Mar 15:47
Compare
Choose a tag to compare

This release brings more CSS Color goodies including the new color-mix() function, gamut mapping for color conversions, support for none components, and more.

color-mix()

The color-mix() function from the CSS Color Level 5 spec allows you to mix two colors together by a specified amount. This works similarly to defining a gradient between two colors, and then choosing the interpolated value somewhere in between.

This syntax is currently available behind a flag in Safari TP, but Parcel CSS parses this function and converts it to a color definition that current browsers can understand.

color-mix(in lch, teal 65%, olive);

results in:

lch(49.4431% 40.4806 162.546);

You get a ton of control over how colors are mixed as well. In addition to choosing how much of each color to mix in, you can choose which color space interpolation occurs in as well as control how hues are mixed. For example:

color-mix(in lch longer hue, teal 65%, olive);

results in:

lch(49.4431% 40.4806 288.546);

Parcel CSS will also convert this color to a legacy RGB color supported by older browsers as well if needed.

Learn more about color-mix() and interpolation in the spec, and try it out!

none components

Colors can now also have components that are defined as none. For example, hsl(none 20% 40%). This defines an HSL color where the hue component is "missing". If rendered directly, this is equivalent to a value of 0. However, these missing components are interpreted differently when mixing colors using color-mix(). In this case, the none component is replaced by the corresponding component in the other color. For example:

color-mix(in hsl, hsl(none 20% 40%), hsl(30deg none 80%));

is equivalent to:

hsl(30deg 20% 60%)

The none components of each color are replaced with the other, and the remaining ones are interpolated.

This can also happen automatically in some cases, when components are deemed "powerless". Read more about how this works in the spec.

Gamut mapping

Some color spaces have a higher color gamut than others. This means the range of valid values is wider, i.e. it can represent more colors. For example, the P3 color space can represent around 25% more colors than sRGB. When converting between color spaces, we need to decide how to handle these "out of gamut" colors. Previously, this was done by clipping. For example, color(display-p3 0 1 0) would become rgb(0, 255, 0). However, this can result in strange behavior in some cases, where you end up with a very different looking color because the relative amounts of each channel changed when one of them is clipped.

Now, Parcel CSS implements Gamut Mapping as defined in the CSS Color spec. This attempts to find a closer color to the intended one within the gamut of the target color space by converting the color to the OKLab color space and adjusting the chroma component (i.e. how "colorful" it is) until the result is within the color gamut and "close enough" to the original color. The above example now converts to rgb(0, 249, 66) instead of rgb(0, 255, 0).

More improvements

  • If you're using the Rust API, each color format is now represented as a struct and you can convert between them easily using Rust's From and Into traits.
  • Color fallback generation is improved so that we generate fewer fallbacks when not needed.
  • Fixed the order of border and border-image declarations in generated code.

v1.5.0

02 Mar 15:31
Compare
Choose a tag to compare

This release adds support for the CSS Color Level 4 spec, which enables many new ways to define colors including high gamut (e.g. HDR) color spaces. Currently, these are only implemented in Safari, but Parcel CSS can now compile them to older sRGB colors supported across all browsers automatically.

The supported functions are:

  • lab() and lch() – these are device independent color spaces which can represent the entire human visual spectrum. Currently supported in Safari 15.
  • oklab() and oklch() – an improved version of the lab and lch color spaces. Available in Safari TP.
  • color() – provides a way to use pre-defined color spaces such as Display P3 (supported since Safari 10), rec2020, and CIE XYZ. All specified color spaces are supported.

This screenshot shows the difference between sRGB and lab in terms of color gamut. The lab version is much more vibrant when displayed on modern hardware with high color gamut support.

image

Parcel CSS will compile these colors according to your browser targets. When a browser doesn't support them, duplicate fallback declarations will be created containing the equivalent sRGB color. The original color will also be included so that browsers that support it will get a higher color gamut. If a lower version of Safari is included that doesn't support Lab but does support P3, a Display P3 version will also be included as it includes a higher color gamut than sRGB.

For example:

.foo {
  color: oklab(59.686% 0.1009 0.1192);
}

becomes:

.foo {
  color: #c65d07;
  color: color(display-p3 .724144 .386777 .148795);
  color: lab(52.2319% 40.1449 59.9171);
}

In addition, Parcel CSS also supports these colors when used within custom properties, or in declarations that use var() references. In these cases, fallbacks cannot be done with duplicate declarations in the same rule. Instead, Parcel CSS outputs a duplicate rule within an @supports block.

.foo {
  text-shadow: var(--x) lab(29.2345% 39.3825 20.0664);
  --foo: lab(29.2345% 39.3825 20.0664);
}

becomes:

.foo {
  text-shadow: var(--x) #7d2329;
  --foo: #7d2329;
}

@supports (color: color(display-p3 0 0 0)) {
  .foo {
    text-shadow: var(--x) color(display-p3 .451706 .165516 .1701);
    --foo: color(display-p3 .451706 .165516 .1701);
  }
}

@supports (color: lab(0% 0 0)) {
  .foo {
    text-shadow: var(--x) lab(29.2345% 39.3825 20.0664);
    --foo: lab(29.2345% 39.3825 20.0664);
  }
}

Try it out here.

To learn more about these new color spaces, check out this article, and play around with this lab color picker.

v1.4.0

23 Feb 15:53
Compare
Choose a tag to compare

This release add some new features, including support for unicode-range syntax, cascade layers, and the @property rule. There is also a fix to the order in which CSS files are bundled to be correct according to the spec and browser behavior.

Unicode range

The unicode-range property within an @font-face rule declares what characters a font supports. Parcel CSS now supports parsing and minifying this syntax. For example, U+2000-20FF minifies as U+20??. Example

Cascade layers

Parcel CSS now supports parsing, minifying, and bundling cascade layers including the @layer rule and layers defined within @import rules. When bundling an @import with a layer, the rules within that dependency are wrapped in an @layer rule. For example:

/* a.css */
@import "b.css" layer(foo);
.a { color: red }

/* b.css */
.b { color: green }

becomes:

@layer foo {
  .b {
    color: green;
  }
}

.a {
  color: red;
}

Bundling order

Cascade layers also introduced a change to the way bundling must occur to preserve correctness. @layer rules are one of the few rules that are allowed before @import rules, so we can no longer simply concatenate files together. The imported CSS must be inlined where the @import rule was seen, preserving the @layer rules before.

This also uncovered a bug in the bundling logic. If a CSS file is imported twice, the last occurrence should be preserved rather than the first. For example:

/* index.css */
@import "a.css";
@import "b.css";
@import "a.css";

/* a.css */
body { background: green; }

/* b.css */
body { background: red; }

In this example, the body should be green, but the previous bundling behavior made it red. This might seem unexpected, as a number of CSS bundlers implement this incorrectly, taking the first instance rather than the last. But in browsers, both @import "a.css" rules are evaluated, so the last one wins. Now Parcel CSS matches browser behavior here as well.

@property rule

The @property rule allows you to register the syntax for custom properties, so that they may be type checked, have a default value, and control inheritance. For example:

@property --property-name {
  syntax: '<color>';
  inherits: false;
  initial-value: #c0ffee;
}

This defines a custom property named --property-name, which accepts colors, has an initial value of #c0ffee and is not inherited.

Parcel CSS can now parse, validate and minify this rule. This includes parsing the syntax property and validating that the initial-value parses according to it. The initial-value and syntax are also minified accordingly. Here's a live example.