Releases: parcel-bundler/lightningcss
v1.9.0
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 inSourceProvider
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
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
v1.8.2
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
Fixed
- Compile
text-decoration-thickness
percentage to acalc
for non-supporting browsers. - Don't include
text-decoration-thickness
intext-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
- Add support for selecting a version of
@parcel/css
in the playground. Thanks @ahabhgk!
v1.8.0
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 thedir
HTML attribute and thedirection
CSS property, but:lang
is only affected by thelang
HTML attribute. When setting thedir
HTML attribute thelang
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
andToCss
traits are now exposed, which allows you to parse and serialize individual CSS rules and values. See #140 for an example. Errors also now implementstd::error::Error
.
Fixes
v1.7.4
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
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 thevar()
is used, not where the custom property is defined. Therefore, without actually applying allvar()
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
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
andInto
traits. - Color fallback generation is improved so that we generate fewer fallbacks when not needed.
- Fixed the order of
border
andborder-image
declarations in generated code.
v1.5.0
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()
andlch()
– these are device independent color spaces which can represent the entire human visual spectrum. Currently supported in Safari 15.oklab()
andoklch()
– 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.
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
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.