diff --git a/.gitattributes b/.gitattributes
index 9dbabfe0..8e82d9b5 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -28,5 +28,9 @@
# do not replace lf by crlf
*.css text
*.json text
+*.css text
+*.js text
+*.ts text
+*.sh text
text eol=lf
diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml
index 34d4bd29..f2d0597b 100644
--- a/.github/workflows/coverage.yml
+++ b/.github/workflows/coverage.yml
@@ -6,8 +6,8 @@ jobs:
runs-on: ubuntu-latest
steps:
# Your original steps
- - uses: actions/checkout@v4
- - uses: actions/setup-node@v4
+ - uses: actions/checkout@v7
+ - uses: actions/setup-node@v6
- name: Install
run: npm install
- name: Test and Coverage
diff --git a/.github/workflows/jsr.yml b/.github/workflows/jsr.yml
index b294277b..6fd7c3cf 100644
--- a/.github/workflows/jsr.yml
+++ b/.github/workflows/jsr.yml
@@ -14,5 +14,5 @@ jobs:
contents: read
id-token: write # The OIDC ID token is used for authentication with JSR.
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v7
- run: npx jsr publish
\ No newline at end of file
diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml
index 0fe18abe..b583ada5 100644
--- a/.github/workflows/node.yml
+++ b/.github/workflows/node.yml
@@ -17,13 +17,13 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [ 20.x, 22.x, 24.x ]
+ node-version: [ 22.x, 24.x, 26.x ]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v7
- name: Use Node.js ${{ matrix.node-version }}
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
- name: Install NPM dependencies
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
new file mode 100644
index 00000000..a829dd79
--- /dev/null
+++ b/.github/workflows/windows.yml
@@ -0,0 +1,33 @@
+# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
+# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
+
+name: Node.js Windows CI
+
+on:
+ push:
+ branches: [ "master" ]
+ pull_request:
+ branches: [ "master" ]
+
+jobs:
+ build:
+
+ runs-on: windows-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ node-version: [ 22.x, 24.x, 26.x ]
+ # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
+
+ steps:
+ - uses: actions/checkout@v7
+ - name: Use Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v6
+ with:
+ node-version: ${{ matrix.node-version }}
+ - name: Install NPM dependencies
+ run: npm install
+ - name: Install playwright dependencies
+ run: npx playwright install --with-deps
+ - run: npm run 'test:node'
diff --git a/.gitignore b/.gitignore
index 9c1da5c1..a4f29e5e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,11 @@ test/*.html
test/*.json
test/*.md
/docs
+/deno.lock
+/badges
+/.nyc_output
+/.repl_history
+/CPU.*
# Logs
logs
diff --git a/.npmignore b/.npmignore
index 475530af..644183ba 100644
--- a/.npmignore
+++ b/.npmignore
@@ -31,4 +31,8 @@
/test/css-modules/demo.css
/test/img/
/test/main.mjs
-/test/modules/
\ No newline at end of file
+/test/modules/
+/playground
+/deno.lock
+/scripts
+/typedoc.config.js
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7cdd71b3..6a0d0cdc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,198 @@
# Changelog
-## v1.4.3
+## v1.4.4
+
+### Validation changes
+
+- [x] Added the `state` and `errors` properties to nodes to expose their validation status and any associated validation errors.
+- [x] Nodes that fail validation are no longer discarded. Nodes are discarded only if they are invalid, unparsed, or malformed. Unknown nodes are discarded when lenient parsing is disabled.
+
+### Color
+
+- [x] `alpha()`: Added support for minifying the CSS Color 5 `alpha()` color function.
+
+```ts
+
+import {transform} from '@tbela99/css-parser';
+
+const css = `
+
+:root {
+--mycolor: oklch(60% 0.25 315 / 0.8);
+}
+ .s {
+
+ color:
+ alpha(from var(--mycolor) / calc(alpha * 0.5));
+
+`;
+
+const result = await transform(css, {
+ beautify: true,
+ inlineCssVariables: true,
+ });
+
+console.debug(result.code);
+
+```
+
+result:
+```css
+.s {
+ color: #b538e366
+}
+```
+
+- [x] `color-mix()`: Implemented support for the updated `color-mix()` syntax defined in https://www.w3.org/TR/css-values-5/#color-mix
+
+
+```ts
+
+import {transform} from '@tbela99/css-parser';
+
+const css = `
+.hsl { color: color-mix(firebrick, goldenrod); }
+`;
+
+const result = await transform(css, {
+ beautify: true,
+ convertColor: ColorType.OKLAB
+ });
+
+console.debug(result.code);
+
+```
+
+result:
+
+```css
+.hsl {
+ color: oklab(.624172 .087878 .113592)
+}
+```
+
+### Webkit gradient prefix removal
+
+- [x] Convert -webkit-* gradient functions
+ - [x] -webkit-gradient() to:
+ - [x] linear-gradient()
+ - [ ] radial-gradient() - not supported ⚠️
+ - [x] -webkit-linear-gradient()
+ - [x] -webkit-repeating-linear-gradient()
+ - [x] -webkit-radial-gradient()
+ - [x] -webkit-repeating-radial-gradient()
+
+### CSS gradient minification
+
+- [x] Minify gradient functions
+ - [x] linear-gradient()
+ - [x] radial-gradient()
+ - [x] conic-gradient()
+ - [x] repeating-linear-gradient()
+ - [x] repeating-radial-gradient()
+ - [x] repeating-conic-gradient()
+
+### Other changes
+
+- [x] Minify transform-origin property
+- [x] Changing oklab distance threshold to `0.00001` according to https://drafts.csswg.org/css-color-4/#comparing-color-values
+
+### Bug fixes
+- [x] Fix path resolution on Windows
+- [x] Do not minify supports() arguments
+
+CSS incorrectly parsed:
+
+```css
+body {
+ background-color: if(
+ supports(color: oklch(0.7 0.185 232)): oklch(0.7 0.185 232);
+ else: #00adf3;
+ );
+
+ &::after {
+ content: if(
+ supports(color: oklch(0.7 0.185 232)): "Your browser supports OKLCH";
+ else: "Your browser does not support OKLCH";
+ );
+ }
+}
+```
+
+result:
+
+```css
+body {
+ background-color: if(supports(color:#00aefc):#00aefc;else:#00adf3);
+ &:after {
+ content: if(supports(color:#00aefc):"Your browser supports OKLCH";else:"Your browser does not support OKLCH")
+ }
+}
+```
+
+instead of:
+
+```css
+body {
+ background-color: #00adf3;
+ @supports (color:oklch(.7 .185 232)) {
+ background-color: #00aefc
+ }
+ &:after {
+ content: "Your browser does not support OKLCH";
+ @supports (color:oklch(.7 .185 232)) {
+ content: "Your browser supports OKLCH"
+ }
+ }
+}
+```
+
+- [x] Incorrectly parse selector when removePrefix and css module settings are enabled
+
+```css
+*,:before,:after {
+ box-sizing: border-box
+}
+```
+is parsed and rendered as:
+
+```css
+,,*:before:after {
+ box-sizing: border-box
+}
+```
+
+instead of:
+
+```css
+*,:before,:after {
+ box-sizing: border-box
+}
+```
+
+### Deprecation
+
+#### Loading as commonjs module
+- [x] Deprecate commonjs entry point. It will be removed in a future release.
+
+#### Functions
+- [x] Deprecate `parseFile()`. Use `parse({file, asStream, ...options})` instead.
+- [x] Deprecate `transformFile()`. Use `transform({file, asStream, ...options})` instead
+
+#### Enums
+- [x] Deprecate using `ValidationLevel` to configure parsing validation settings. Use `boolean` values instead.
+- [x] Deprecate invalid `EnumToken` node types. There are no longer used.
+- [x] `EnumToken.InvalidRuleNodeType`
+- [x] `EnumToken.InvalidAtRuleNodeType`
+- [x] `EnumToken.InvalidDeclarationNodeType`
+
+#### Interfaces
+- [x] `AstInvalidRule`
+- [x] `AstInvalidDeclaration`
+- [x] `AstInvalidAtRule`
+
+## v1.4.3
### CSS Modules
@@ -14,7 +205,6 @@ result = await transform(css, {
beautify: true,
module: {scoped: ModuleScopeEnumOptions.Shortest
}
-
});
console.log(result.code);
diff --git a/README.md b/README.md
index 80817d8f..6e9655d0 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@
[](https://tbela99.github.io/css-parser/playground/) [](https://www.npmjs.com/package/@tbela99/css-parser) [](https://jsr.io/@tbela99/css-parser) [](https://github.com/tbela99/css-parser/actions) [](https://tbela99.github.io/css-parser/docs) [](https://www.npmjs.com/package/@tbela99/css-parser) [](https://www.npmjs.com/package/@tbela99/css-parser)
+)](https://jsr.io/@tbela99/css-parser) [](https://github.com/tbela99/css-parser/actions) [](https://tbela99.github.io/css-parser/docs) [](https://www.npmjs.com/package/@tbela99/css-parser) [](https://www.npmjs.com/package/@tbela99/css-parser)
# css-parser
-CSS parser, minifier and validator for node and the browser
+CSS parser, transformer, minifier and validator for node and the browser
## Installation
@@ -22,852 +22,68 @@ $ deno add @tbela99/css-parser
## Features
-- no dependency
-- CSS validation based upon mdn-data
-- CSS modules support
-- fault-tolerant parser implementing the CSS syntax module 3 recommendations.
-- fast and efficient minification without unsafe transforms,
- see [benchmark](https://tbela99.github.io/css-parser/benchmark/index.html)
-- colors minification: color(), lab(), lch(), oklab(), oklch(), color-mix(), light-dark(), system colors and
- relative color
-- color conversion to any supported color format
-- automatic nested css rules generation
-- nested css rules conversion to legacy syntax
-- convert css if() function to legacy syntax
-- sourcemap generation
-- css shorthands computation. see the supported properties list below
-- css transform functions minification
-- css math functions evaluation: calc(), clamp(), min(), max(), etc.
-- css variables inlining
-- duplicate properties removal
-- @import rules flattening
-- experimental CSS prefix removal
-
-## Online documentation
-
-See the full documentation at the [CSS Parser](https://tbela99.github.io/css-parser/docs) documentation site
+* **Zero dependencies** — lightweight and easy to integrate into any project.
+* **Standards-based CSS validation** powered by MDN data.
+* **Full CSS Modules support** for modern component-based workflows.
+* **Fault-tolerant parsing** that follows the CSS Syntax Module Level 3 specification.
+* **High-performance minification** with safe optimizations and no unsafe transforms.
+* **Advanced color processing** with support for modern color spaces and functions, including `color()`, `lab()`, `lch()`, `oklab()`, `oklch()`, `color-mix()`, `light-dark()`, system colors, and relative colors.
+* **Color conversion engine** capable of transforming colors between all supported formats.
+* **Automatic CSS nesting generation** from compatible selectors.
+* **Source map generation** for easier debugging and development workflows.
+* **Shorthand property computation** to reduce output size and improve optimization.
+* **Transform function optimization** for more compact CSS output.
+* **Math function evaluation**, including `calc()`, `clamp()`, `min()`, `max()`, and related expressions.
+* **CSS variable inlining** where values can be safely resolved.
+* **Duplicate declaration removal** to eliminate redundant rules.
+* **`@import` flattening** to produce self-contained stylesheets.
+
+## Vendor prefix removal
+**Experimental vendor prefix cleanup** to modernize generated CSS.
+
+## Syntax lowering
+CSS-Parser can transform these modern CSS features into lower-level CSS syntax:
+* **Nested CSS transpilation** to legacy-compatible syntax.
+* **`if()` function transpilation** for broader browser compatibility.
+* **`color-mix()` function conversion** convert new color-mix() syntax to any of the supported colors.
+
+## Benchmark
+
+Across all tested datasets, css-parser consistently produces the smallest output among the benchmarked tools, including Lightning CSS, cssnano, csso, clean-css, css-tree, and esbuild.
+
+The [benchmark](https://tbela99.github.io/css-parser/benchmark/index.html) evaluates minification effectiveness on a diverse set of real-world stylesheets, including Bootstrap 4, Bootstrap 5, Tailwind CSS, Animate.css, Foundation, Font Awesome, Normalize.css, and others.
+
+A sample result:
+
+| File | ligthningcss | CSS Parser |
+| ------------------------------ | ------------- | ------------- |
+| tailwind.css - 2380419 bytes | 1864728 bytes | 1633251 bytes |
+| bootstrap-4.css - 200078 bytes | 153616 bytes | 144700 bytes |
+| bootstrap-5.css - 205481 bytes | 159987 bytes | 151258 bytes |
+
+On the [complete benchmark suite](https://tbela99.github.io/css-parser/benchmark/index.html), css-parser generated a total output size of 2,204,888 bytes, compared to 2,494,113 bytes for Lightning CSS and larger outputs for all other tested minifiers.
+While some tools prioritize raw execution speed, css-parser focuses on maximizing compression while preserving stylesheet semantics, resulting in consistently smaller production bundles.
## Playground
Try it [online](https://tbela99.github.io/css-parser/playground/)
-## Import
-
-There are several ways to import the library into your application.
-
-### Node
-
-import as a module
-
-```javascript
-
-import {transform} from '@tbela99/css-parser';
-
-// ...
-```
-
-### Deno
-
-import as a module
-
-```javascript
-
-import {transform} from '@tbela99/css-parser';
-
-// ...
-```
-
-import as a CommonJS module
-
-```javascript
-
-const {transform} = require('@tbela99/css-parser/cjs');
-
-// ...
-```
-
-### Web
-
-Programmatic import
-
-```javascript
-
-import {transform} from '@tbela99/css-parser/web';
-
-// ...
-```
-
-Javascript module from cdn
-
-```html
-
-
-```
-
-Javascript module
-
-```javascript
-
-
-```
-
-Javascript umd module from cdn
-
-```html
-
-
-
-```
-
-## Exported functions
-
-```ts
-
-/* parse and render css */
-transform(css: string | ReadableStream, options?: TransformOptions = {}): Promise
-/* parse css */
-parse(css: string | ReadableStream, options?: ParseOptions = {}): Promise;
-/* render ast */
-render(ast: AstNode, options?: RenderOptions = {}): RenderResult;
-/* parse and render css file or url */
-transformFile(filePathOrUrl: string, options?: TransformOptions = {}, asStream?: boolean): Promise;
-/* parse css file or url */
-parseFile(filePathOrUrl: string, options?: ParseOptions = {}, asStream?: boolean): Promise;
-
-```
-
-## Transform
-
-Parse and render css in a single pass.
-
-### Example
-
-```javascript
-
-import {transform} from '@tbela99/css-parser';
-
-const {ast, code, map, errors, stats} = await transform(css, {minify: true, resolveImport: true, cwd: 'files/css'});
-```
-
-### Example
-
-Read from stdin with node using readable stream
-
-```typescript
-import {transform} from "../src/node";
-import {Readable} from "node:stream";
-import type {TransformResult} from '../src/@types/index.d.ts';
-
-const readableStream: ReadableStream = Readable.toWeb(process.stdin) as ReadableStream;
-const result: TransformResult = await transform(readableStream, {beautify: true});
-
-console.log(result.code);
-```
-
-### TransformOptions
-
-Include ParseOptions and RenderOptions
-
-#### ParseOptions
-
-> Minify Options
-
-- minify: boolean, optional. default to _true_. optimize ast.
-- pass: number, optional. minification passes. default to 1
-- nestingRules: boolean, optional. automatically generated nested rules.
-- expandNestingRules: boolean, optional. convert nesting rules into separate rules. will automatically set nestingRules
- to false.
-- expandIfSyntax: experimental, convert css if() function into legacy syntax.
-- removeDuplicateDeclarations: boolean, optional. remove duplicate declarations.
-- computeTransform: boolean, optional. compute css transform functions.
-- computeShorthand: boolean, optional. compute shorthand properties.
-- computeCalcExpression: boolean, optional. evaluate calc() expression
-- inlineCssVariables: boolean, optional. replace some css variables with their actual value. they must be declared once
- in the :root {} or html {} rule.
-- removeEmpty: boolean, optional. remove empty rule lists from the ast.
-
-> CSS modules Options
-
-- module: boolean | ModuleCaseTransformEnum | ModuleScopeEnumOptions | ModuleOptions, optional. css modules options.
-
-> CSS Prefix Removal Options
-
-- removePrefix: boolean, optional. remove CSS prefixes.
-
-> Validation Options
-
-- validation: ValidationLevel | boolean, optional. enable validation. permitted values are:
- - ValidationLevel.None: no validation
- - ValidationLevel.Default: validate selectors and at-rules (default)
- - ValidationLevel.All. validate all nodes
- - true: same as ValidationLevel.All.
- - false: same as ValidationLevel.None
-- lenient: boolean, optional. preserve invalid tokens.
-
-> Sourcemap Options
-
-- src: string, optional. original css file location to be used with sourcemap, also used to resolve url().
-- sourcemap: boolean, optional. preserve node location data.
-
-> Ast Traversal Options
-
-- visitor: VisitorNodeMap, optional. node visitor used to transform the ast.
-
-> Urls and \@import Options
-
-- resolveImport: boolean, optional. replace @import rule by the content of the referenced stylesheet.
-- resolveUrls: boolean, optional. resolve css 'url()' according to the parameters 'src' and 'cwd'
-
-> Misc Options
-- removeCharset: boolean, optional. remove @charset.
-- cwd: string, optional. destination directory used to resolve url().
-- signal: AbortSignal, optional. abort parsing.
-
-#### RenderOptions
-
-> Minify Options
-
-- beautify: boolean, optional. default to _false_. beautify css output.
-- minify: boolean, optional. default to _true_. minify css values.
-- withParents: boolean, optional. render this node and its parents.
-- removeEmpty: boolean, optional. remove empty rule lists from the ast.
-- expandNestingRules: boolean, optional. expand nesting rules.
-- preserveLicense: boolean, force preserving comments starting with '/\*!' when minify is enabled.
-- removeComments: boolean, remove comments in generated css.
-- convertColor: boolean | ColorType, convert colors to the specified color. default to ColorType.HEX. supported values are:
- - true: same as ColorType.HEX
- - false: no color conversion
- - ColorType.HEX
- - ColorType.RGB or ColorType.RGBA
- - ColorType.HSL
- - ColorType.HWB
- - ColorType.CMYK or ColorType.DEVICE_CMYK
- - ColorType.SRGB
- - ColorType.SRGB_LINEAR
- - ColorType.DISPLAY_P3
- - ColorType.PROPHOTO_RGB
- - ColorType.A98_RGB
- - ColorType.REC2020
- - ColorType.XYZ or ColorType.XYZ_D65
- - ColorType.XYZ_D50
- - ColorType.LAB
- - ColorType.LCH
- - ColorType.OKLAB
- - ColorType.OKLCH
-
-> Sourcemap Options
-
-- sourcemap: boolean | 'inline', optional. generate sourcemap.
-
-> Misc Options
-
-- indent: string, optional. css indention string. uses space character by default.
-- newLine: string, optional. new line character.
-- output: string, optional. file where to store css. url() are resolved according to the specified value. no file is
- created though.
-- cwd: string, optional. destination directory used to resolve url().
-
-## Parsing
-
-### Usage
-
-```javascript
-
-parse(css, parseOptions = {})
-```
-
-### Example
-
-````javascript
-
-const {ast, errors, stats} = await parse(css);
-````
-
-## Rendering
-
-### Usage
-
-```javascript
-render(ast, RenderOptions = {});
-```
-
-### Examples
-
-Rendering ast
-
-```javascript
-import {parse, render} from '@tbela99/css-parser';
-
-const css = `
-@media screen and (min-width: 40em) {
- .featurette-heading {
- font-size: 50px;
- }
- .a {
- color: red;
- width: 3px;
- }
-}
-`;
-
-const result = await parse(css, options);
-
-// print declaration without parents
-console.error(render(result.ast.chi[0].chi[1].chi[1], {withParents: false}));
-// -> width:3px
-
-// print declaration with parents
-console.debug(render(result.ast.chi[0].chi[1].chi[1], {withParents: true}));
-// -> @media screen and (min-width:40em){.a{width:3px}}
-
-```
-
-### CSS Modules
-
-CSS modules features are fully supported. refer to the [CSS modules](https://tbela99.github.io/css-parser/docs/documents/Guide.CSS_modules.html) documentation for more information.
-
-```javascript
-import {transform} from '@tbela99/css-parser';
-
-const css = `
-.table {
- border-collapse: collapse;
- width: 100%;
-}
-
-.table td, .table th {
- border: 1px solid #ddd;
- padding: 8px;
-}
-
-.table tr:nth-child(even){background-color: #f2f2f2;}
-
-.table tr:hover {background-color: #ddd;}
-
-.table th {
- padding-top: 12px;
- padding-bottom: 12px;
- text-align: left;
- background-color: #4CAF50;
- color: white;
-}
-`;
-
-const result = await transform(css, {module: true});
-
-// css code
-console.log(result.code);
-// css mapping
-console.log(result.mapping);
-```
-
-### Convert colors
-
-```javascript
-import {transform, ColorType} from '@tbela99/css-parser';
-
-
-const css = `
-.hsl { color: #b3222280; }
-`;
-const result: TransformResult = await transform(css, {
- beautify: true,
- convertColor: ColorType.SRGB
-});
-
-console.log(result.css);
-
-```
-
-result
-
-```css
-.hsl {
- color: color(srgb .7019607843137254 .13333333333333333 .13333333333333333/50%)
-}
-```
-
-### Merge similar rules
-
-CSS
-
-```css
-
-.clear {
- width: 0;
- height: 0;
- color: transparent;
-}
-
-.clearfix:before {
-
- height: 0;
- width: 0;
-}
-```
-
-```javascript
-
-import {transform} from '@tbela99/css-parser';
-
-const result = await transform(css);
-
-```
-
-Result
-
-```css
-.clear, .clearfix:before {
- height: 0;
- width: 0
-}
-
-.clear {
- color: #0000
-}
-```
-
-### Automatic CSS Nesting
-
-CSS
-
-```javascript
-const {parse, render} = require("@tbela99/css-parser/cjs");
-
-const css = `
-table.colortable td {
- text-align:center;
-}
-table.colortable td.c {
- text-transform:uppercase;
-}
-table.colortable td:first-child, table.colortable td:first-child+td {
- border:1px solid black;
-}
-table.colortable th {
- text-align:center;
- background:black;
- color:white;
-}
-`;
-
-const result = await parse(css, {nestingRules: true}).then(result => render(result.ast, {minify: false}).code);
-```
-
-Result
-
-```css
-table.colortable {
- & td {
- text-align: center;
-
- &.c {
- text-transform: uppercase
- }
-
- &:first-child, &:first-child + td {
- border: 1px solid #000
- }
- }
-
- & th {
- text-align: center;
- background: #000;
- color: #fff
- }
-}
-```
-
-### CSS Validation
-
-CSS
-
-```css
-
-#404 {
- --animate-duration: 1s;
-}
-
-.s, #404 {
- --animate-duration: 1s;
-}
-
-.s [type="text" {
- --animate-duration: 1s;
-}
-
-.s [type="text"]]
-{
- --animate-duration: 1s;
-}
-
-.s [type="text"] {
- --animate-duration: 1s;
-}
-
-.s [type="text" i] {
- --animate-duration: 1s;
-}
-
-.s [type="text" s]
-{
- --animate-duration: 1s
-;
-}
-
-.s [type="text" b]
-{
- --animate-duration: 1s;
-}
-
-.s [type="text" b],{
- --animate-duration: 1s
-;
-}
-
-.s [type="text" b]
-+ {
- --animate-duration: 1s;
-}
-
-.s [type="text" b]
-+ b {
- --animate-duration: 1s;
-}
-
-.s [type="text" i] + b {
- --animate-duration: 1s;
-}
-
-
-.s [type="text"())]{
- --animate-duration: 1s;
-}
-.s(){
- --animate-duration: 1s;
-}
-.s:focus {
- --animate-duration: 1s;
-}
-```
-
-with validation enabled
-
-```javascript
-import {parse, render} from '@tbela99/css-parser';
-
-const options = {minify: true, validate: true};
-const {code} = await parse(css, options).then(result => render(result.ast, {minify: false}));
-//
-console.debug(code);
-```
-
-```css
-.s:is([type=text],[type=text i],[type=text s],[type=text i]+b,:focus) {
- --animate-duration: 1s
-}
-```
-
-with validation disabled
-
-```javascript
-import {parse, render} from '@tbela99/css-parser';
-
-const options = {minify: true, validate: false};
-const {code} = await parse(css, options).then(result => render(result.ast, {minify: false}));
-//
-console.debug(code);
-```
-
-```css
-.s:is([type=text],[type=text i],[type=text s],[type=text b],[type=text b]+b,[type=text i]+b,:focus) {
- --animate-duration: 1s
-}
-```
-
-### Nested CSS Expansion
-
-CSS
-
-```css
-table.colortable {
- & td {
- text-align: center;
-
- &.c {
- text-transform: uppercase
- }
-
- &:first-child, &:first-child + td {
- border: 1px solid #000
- }
- }
-
- & th {
- text-align: center;
- background: #000;
- color: #fff
- }
-}
-```
-
-Javascript
-
-```javascript
-import {parse, render} from '@tbela99/css-parser';
-
-const options = {minify: true};
-const {code} = await parse(css, options).then(result => render(result.ast, {minify: false, expandNestingRules: true}));
-//
-console.debug(code);
-```
-
-Result
-
-```css
-
-table.colortable td {
- text-align: center;
-}
-
-table.colortable td.c {
- text-transform: uppercase;
-}
-
-table.colortable td:first-child, table.colortable td:first-child + td {
- border: 1px solid black;
-}
-
-table.colortable th {
- text-align: center;
- background: black;
- color: white;
-}
-```
-
-### CSS if() function expansion
-
-```typescript
-
-const css = `
-button {
- background: linear-gradient(
- if(media(min-width: 768px): to right; else: to bottom),
- if(style(--dark-mode): #333; else: #fff),
- if(style(--dark-mode): #000; else: #ccc)
- );
-}`;
-
-result = await transform(css, {
-
- beautify: true,
- expandIfSyntax: true
- }
-
-});
-
-console.log(result.code);
-```
-
-output
-
-```css
-button {
- background: linear-gradient(to bottom,#fff,#ccc);
- @media (min-width:768px) {
- background: linear-gradient(to right,#fff,#ccc);
- @container style(--dark-mode) {
- background: linear-gradient(to right,#333,#000)
- }
- }
- @container style(--dark-mode) {
- background: linear-gradient(to bottom,#333,#000)
- }
-}
-```
-
-### Calc() resolution
-
-```javascript
-
-import {parse, render} from '@tbela99/css-parser';
-
-const css = `
-a {
-
-width: calc(100px * log(625, 5));
-}
-.foo-bar {
- width: calc(100px * 2);
- height: calc(((75.37% - 63.5px) - 900px) + (2 * 100px));
- max-width: calc(3.5rem + calc(var(--bs-border-width) * 2));
-}
-`;
-
-const prettyPrint = await parse(css).then(result => render(result.ast, {minify: false}).code);
-
-```
-
-result
-
-```css
-a {
- width: 400px;
-}
-
-.foo-bar {
- width: 200px;
- height: calc(75.37% - 763.5px);
- max-width: calc(3.5rem + var(--bs-border-width) * 2)
-}
-```
-
-### CSS variable inlining
-
-```javascript
-
-import {parse, render} from '@tbela99/css-parser';
-
-const css = `
-
-:root {
-
---preferred-width: 20px;
-}
-.foo-bar {
-
- width: calc(calc(var(--preferred-width) + 1px) / 3 + 5px);
- height: calc(100% / 4);}
-`
-
-const prettyPrint = await parse(css, {inlineCssVariables: true}).then(result => render(result.ast, {minify: false}).code);
-
-```
-
-result
-
-```css
-.foo-bar {
- width: 12px;
- height: 25%
-}
-
-```
-
-### CSS variable inlining and relative color
-
-```javascript
-
-import {parse, render} from '@tbela99/css-parser';
-
-const css = `
-
-:root {
---color: green;
-}
-._19_u :focus {
- color: hsl(from var(--color) calc(h * 2) s l);
-
-}
-`
-
-const prettyPrint = await parse(css, {inlineCssVariables: true}).then(result => render(result.ast, {minify: false}).code);
-
-```
-
-result
-
-```css
-._19_u :focus {
- color: navy
-}
-
-```
-
-### CSS variable inlining and relative color
-
-```javascript
-
-import {parse, render} from '@tbela99/css-parser';
-
-const css = `
-
-html { --bluegreen: oklab(54.3% -22.5% -5%); }
-.overlay {
- background: oklab(from var(--bluegreen) calc(1.0 - l) calc(a * 0.8) b);
-}
-`
-
-const prettyPrint = await parse(css, {inlineCssVariables: true}).then(result => render(result.ast, {minify: false}).code);
-
-```
-
-result
-
-```css
-.overlay {
- background: #0c6464
-}
-
-```
-
-# Node Walker
-
-```javascript
-
-import {walk} from '@tbela99/css-parser';
-
-for (const {node, parent, root} of walk(ast)) {
-
- // do something
-}
-```
-
+## Documentation
+
+- [Installation](https://tbela99.github.io/css-parser/docs/documents/Guide.Getting_started.html#installation)
+- [Usage](https://tbela99.github.io/css-parser/docs/documents/Guide.Usage.html)
+ - [Parsing CSS](https://tbela99.github.io/css-parser/docs/documents/Guide.Usage.html#parsing)
+ - [Parsing files](https://tbela99.github.io/css-parser/docs/documents/Guide.Usage.html#parsing-files)
+ - [Parsing streams](https://tbela99.github.io/css-parser/docs/documents/Guide.Usage.html#parsing-from-a-readable-stream)
+ - [Rendering AST](https://tbela99.github.io/css-parser/docs/documents/Guide.Usage.html#rendering-ast)
+ - [Transform](https://tbela99.github.io/css-parser/docs/documents/Guide.Usage.html#transform)
+- [Validation](https://tbela99.github.io/css-parser/docs/documents/Guide.Validation.html)
+- [CSS Modules](https://tbela99.github.io/css-parser/docs/documents/Guide.CSS_Modules.html)
+- [Minification](https://tbela99.github.io/css-parser/docs/documents/Guide.Minification.html)
+- [Custom Transform](https://tbela99.github.io/css-parser/docs/documents/Guide.Custom_Transform.html)
+- [Ast Manipulation](https://tbela99.github.io/css-parser/docs/documents/Guide.Ast_Manipulation.html)
+- [Utility Functions](https://tbela99.github.io/css-parser/docs/documents/Guide.Utility_Functions.html)
+-
## AST
### Comment
@@ -880,18 +96,24 @@ for (const {node, parent, root} of walk(ast)) {
- typ: number
- nam: string, declaration name
- val: array of tokens
+- state: EnumAstNodeStatus, validation state
+- errors: ErrorDescription[], validation errors
### Rule
- typ: number
- sel: string, css selector
- chi: array of children
+- state: EnumAstNodeStatus, validation state
+- errors: ErrorDescription[], validation errors
### AtRule
- typ: number
- nam: string. AtRule name
- val: rule prelude
+- state: EnumAstNodeStatus, validation state
+- errors: ErrorDescription[], validation errors
### AtRuleStyleSheet
@@ -903,32 +125,12 @@ for (const {node, parent, root} of walk(ast)) {
- typ: number
- sel: string, css selector
- chi: array of children
+- state: EnumAstNodeStatus, validation state
+- errors: ErrorDescription[], validation errors
## Sourcemap
-- [x] sourcemap generation
-
-## Minification
-
-- [x] minify keyframes
-- [x] minify transform functions
-- [x] evaluate math functions calc(), clamp(), min(), max(), round(), mod(), rem(), sin(), cos(), tan(), asin(),
- acos(), atan(), atan2(), pow(), sqrt(), hypot(), log(), exp(), abs(), sign()
-- [x] minify colors
-- [x] minify numbers and Dimensions tokens
-- [x] multi-pass minification
-- [x] inline css variables
-- [x] merge identical rules
-- [x] merge adjacent rules
-- [x] compute shorthand: see the list below
-- [x] remove redundant declarations
-- [x] conditionally unwrap :is()
-- [x] automatic css nesting
-- [x] automatically wrap selectors using :is()
-- [x] avoid reparsing (declarations, selectors, at-rule)
-- [x] node and browser versions
-- [x] decode and replace utf-8 escape sequence
-- [x] experimental CSS prefix removal
+- [x] Sourcemap generation
## Computed shorthands properties
@@ -984,257 +186,4 @@ for (const {node, parent, root} of walk(ast)) {
## Performance
-- [x] flatten @import
-
-## Node Transformation
-
-Ast can be transformed using node visitors
-
-### Example 1: Declaration
-
-the visitor is called for any declaration encountered
-
-```typescript
-
-import {AstDeclaration, ParserOptions} from "../src/@types";
-
-const options: ParserOptions = {
-
- visitor: {
-
- Declaration: (node: AstDeclaration) => {
-
- if (node.nam == '-webkit-transform') {
-
- node.nam = 'transform'
- }
- }
- }
-}
-
-const css = `
-
-.foo {
- -webkit-transform: scale(calc(100 * 2/ 15));
-}
-`;
-
-const result = await transform(css, options);
-console.debug(result.code);
-
-// .foo{transform:scale(calc(40/3))}
-```
-
-### Example 2: Declaration
-
-the visitor is called only on 'height' declarations
-
-```typescript
-
-import {AstDeclaration, EnumToken, LengthToken, ParserOptions, transform} from '@tbela99/css-parser';
-
-const options: ParserOptions = {
-
- visitor: {
-
- Declaration: {
-
- // called only for height declaration
- height: (node: AstDeclaration): AstDeclaration[] => {
-
-
- return [
- node,
- {
-
- typ: EnumToken.DeclarationNodeType,
- nam: 'width',
- val: [
- {
- typ: EnumToken.LengthTokenType,
- val: 3,
- unit: 'px'
- }
- ]
- }
- ];
- }
- }
- }
-};
-
-const css = `
-
-.foo {
- height: calc(100px * 2/ 15);
-}
-.selector {
-color: lch(from peru calc(l * 0.8) calc(c * 0.7) calc(h + 180))
-}
-`;
-
-const result = await transform(css, options);
-console.debug(result.code);
-
-// .foo{height:calc(40px/3);width:3px}.selector{color:#0880b0}
-
-```
-
-### Example 3: At-Rule
-
-the visitor is called on any at-rule
-
-```typescript
-
-import {AstAtRule, ParserOptions} from "../src/@types";
-import {transform} from "../src/node";
-
-
-const options: ParserOptions = {
-
- visitor: {
-
- AtRule: (node: AstAtRule): AstAtRule => {
-
- if (node.nam == 'media') {
-
- return {...node, val: 'all'}
- }
- }
- }
-};
-
-const css = `
-
-@media screen {
-
- .foo {
-
- height: calc(100px * 2/ 15);
- }
-}
-`;
-
-const result = await transform(css, options);
-console.debug(result.code);
-
-// .foo{height:calc(40px/3)}
-
-```
-
-### Example 4: At-Rule
-
-the visitor is called only for at-rule media
-
-```typescript
-
-import {AstAtRule, ParserOptions} from "../src/@types";
-import {transform} from "../src/node";
-
-const options: ParserOptions = {
-
- visitor: {
-
- AtRule: {
-
- media: (node: AstAtRule): AstAtRule => {
-
- node.val = 'all';
- return node
- }
- }
- }
-};
-
-const css = `
-
-@media screen {
-
- .foo {
-
- height: calc(100px * 2/ 15);
- }
-}
-`;
-
-const result = await transform(css, options);
-console.debug(result.code);
-
-// .foo{height:calc(40px/3)}
-
-```
-
-### Example 5: Rule
-
-the visitor is called on any Rule
-
-```typescript
-
-import {AstAtRule, ParserOptions} from "../src/@types";
-import {transform} from "../src/node";
-
-const options: ParserOptions = {
-
- visitor: {
-
- Rule(node: AstRule): AstRule {
-
- node.sel = '.foo,.bar,.fubar'
- return node;
- }
- }
-};
-
-const css = `
-
- .foo {
-
- height: calc(100px * 2/ 15);
- }
-`;
-
-const result = await transform(css, options);
-console.debug(result.code);
-
-// .foo,.bar,.fubar{height:calc(40px/3)}
-
-```
-
-### Example 6: Rule
-
-Adding declarations to any rule
-
-```typescript
-
-import {AstRule, parseDeclarations, ParserOptions, transform} from '@tbela99/css-parser';
-
-const options: ParserOptions = {
-
- removeEmpty: false,
- visitor: {
-
- Rule: async (node: AstRule): Promise => {
-
- if (node.sel == '.foo') {
-
- node.chi.push(...await parseDeclarations('width: 3px'));
- return node;
- }
-
- return null;
- }
- }
-};
-
-const css = `
-
-.foo {
-}
-`;
-
-const result = await transform(css, options);
-console.debug(result.code);
-
-// .foo{width:3px}
-
-```
+- [x] Bundle file referenced by the @import statement.
diff --git a/dist/config.json.js b/dist/config.json.js
index 1db687dc..12c7300d 100644
--- a/dist/config.json.js
+++ b/dist/config.json.js
@@ -1503,9 +1503,50 @@ var map = {
shorthand: "background"
}
};
+var property = {
+ "transform-origin": {
+ pattern: [
+ [
+ "left|center|right",
+ "top|center|bottom",
+ ""
+ ],
+ [
+ "left|center|right",
+ "top|center|bottom"
+ ],
+ [
+ "center|left|right|center|top|bottom|"
+ ]
+ ],
+ mapping: {
+ center: {
+ typ: "Perc",
+ val: 50
+ },
+ left: {
+ typ: "Perc",
+ val: 0
+ },
+ right: {
+ typ: "Perc",
+ val: 100
+ },
+ top: {
+ typ: "Perc",
+ val: 0
+ },
+ bottom: {
+ typ: "Perc",
+ val: 100
+ }
+ }
+ }
+};
var config = {
properties: properties,
- map: map
+ map: map,
+ property: property
};
-export { config as default, map, properties };
+export { config as default, map, properties, property };
diff --git a/dist/index-umd-web.js b/dist/index-umd-web.js
index bc6dd52d..8e9501bb 100644
--- a/dist/index-umd-web.js
+++ b/dist/index-umd-web.js
@@ -5,7 +5,7 @@
})(this, (function (exports) { 'use strict';
/**
- * syntax validation enum
+ * Syntax validation enum
*/
var SyntaxValidationResult;
(function (SyntaxValidationResult) {
@@ -17,7 +17,46 @@
SyntaxValidationResult[SyntaxValidationResult["Lenient"] = 2] = "Lenient";
})(SyntaxValidationResult || (SyntaxValidationResult = {}));
/**
- * enum of validation levels
+ * Enum of node statuses
+ */
+ exports.EnumAstNodeStatus = void 0;
+ (function (EnumAstNodeStatus) {
+ /**
+ * Node passed validation
+ */
+ EnumAstNodeStatus[EnumAstNodeStatus["Validated"] = 0] = "Validated";
+ /**
+ * Node is invalid
+ */
+ EnumAstNodeStatus[EnumAstNodeStatus["Invalid"] = 1] = "Invalid";
+ /**
+ * node is not validated
+ */
+ EnumAstNodeStatus[EnumAstNodeStatus["Unvalidated"] = 2] = "Unvalidated";
+ /**
+ * Node did not pass validation, but is preserved.
+ */
+ EnumAstNodeStatus[EnumAstNodeStatus["ValidationFailed"] = 3] = "ValidationFailed";
+ /**
+ * Parsing the node is not supported yet or the node syntax definition does not exist.
+ */
+ EnumAstNodeStatus[EnumAstNodeStatus["Unknown"] = 4] = "Unknown";
+ /**
+ * Failed to parse the node.
+ */
+ EnumAstNodeStatus[EnumAstNodeStatus["Unparsed"] = 5] = "Unparsed";
+ /**
+ * Node is disallowed in the context
+ */
+ EnumAstNodeStatus[EnumAstNodeStatus["Disallowed"] = 6] = "Disallowed";
+ /**
+ * Node is malformed
+ */
+ EnumAstNodeStatus[EnumAstNodeStatus["Malformed"] = 7] = "Malformed";
+ })(exports.EnumAstNodeStatus || (exports.EnumAstNodeStatus = {}));
+ /**
+ * Enum of validation levels
+ * @deprecated
*/
exports.ValidationLevel = void 0;
(function (ValidationLevel) {
@@ -45,9 +84,13 @@
* validate selectors, at-rules and declarations
*/
ValidationLevel[ValidationLevel["All"] = 7] = "All";
+ /**
+ * Report only. Apply validation and report nodes that are marked as invalid
+ */
+ ValidationLevel[ValidationLevel["ReportOnly"] = 8] = "ReportOnly";
})(exports.ValidationLevel || (exports.ValidationLevel = {}));
/**
- * enum of all token types
+ * Enum of all token types
*/
exports.EnumToken = void 0;
(function (EnumToken) {
@@ -378,7 +421,8 @@
*/
EnumToken[EnumToken["NestingSelectorTokenType"] = 80] = "NestingSelectorTokenType";
/**
- * invalid rule token type
+ * Invalid rule token type
+ * @deprecated
*/
EnumToken[EnumToken["InvalidRuleNodeType"] = 81] = "InvalidRuleNodeType";
/**
@@ -390,7 +434,8 @@
*/
EnumToken[EnumToken["InvalidAttrTokenType"] = 83] = "InvalidAttrTokenType";
/**
- * invalid at rule token type
+ * Invalid at rule token type
+ * @deprecated
*/
EnumToken[EnumToken["InvalidAtRuleNodeType"] = 84] = "InvalidAtRuleNodeType";
/**
@@ -430,7 +475,8 @@
*/
EnumToken[EnumToken["KeyframesAtRuleNodeType"] = 93] = "KeyframesAtRuleNodeType";
/**
- * invalid declaration node type
+ * invalid declaration node type.
+ * @deprecated
*/
EnumToken[EnumToken["InvalidDeclarationNodeType"] = 94] = "InvalidDeclarationNodeType";
/* css module nodes */
@@ -727,7 +773,7 @@
EnumToken[EnumToken["TimelineFunction"] = 16] = "TimelineFunction";
})(exports.EnumToken || (exports.EnumToken = {}));
/**
- * supported color types enum
+ * Supported color types enum
*/
exports.ColorType = void 0;
(function (ColorType) {
@@ -831,6 +877,10 @@
* custom color
*/
ColorType[ColorType["CUSTOM_COLOR"] = 24] = "CUSTOM_COLOR";
+ /**
+ * alpha() color function
+ */
+ ColorType[ColorType["ALPHA"] = 25] = "ALPHA";
/**
* alias for rgba
*/
@@ -848,6 +898,9 @@
*/
ColorType[ColorType["DEVICE_CMYK"] = 7] = "DEVICE_CMYK";
})(exports.ColorType || (exports.ColorType = {}));
+ /**
+ * Supported module case transform
+ */
exports.ModuleCaseTransformEnum = void 0;
(function (ModuleCaseTransformEnum) {
/**
@@ -871,6 +924,9 @@
*/
ModuleCaseTransformEnum[ModuleCaseTransformEnum["DashCaseOnly"] = 16] = "DashCaseOnly";
})(exports.ModuleCaseTransformEnum || (exports.ModuleCaseTransformEnum = {}));
+ /**
+ * Supported module scope
+ */
exports.ModuleScopeEnumOptions = void 0;
(function (ModuleScopeEnumOptions) {
/**
@@ -911,6 +967,11 @@
ModuleScopeEnumOptions[ModuleScopeEnumOptions["Shortest"] = 512] = "Shortest";
})(exports.ModuleScopeEnumOptions || (exports.ModuleScopeEnumOptions = {}));
// https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Values_and_units#absolute_length_units
+ /**
+ * Convert length to px
+ * @param value
+ * @returns
+ */
function length2Px(value) {
let result = null;
if (value.typ == exports.EnumToken.NumberTokenType) {
@@ -972,7 +1033,7 @@
}
/**
- * options for the walk function
+ * Options for the walk function
*/
exports.WalkerOptionEnum = void 0;
(function (WalkerOptionEnum) {
@@ -994,7 +1055,7 @@
WalkerOptionEnum[WalkerOptionEnum["IgnoreChildren"] = 8] = "IgnoreChildren";
})(exports.WalkerOptionEnum || (exports.WalkerOptionEnum = {}));
/**
- * event types for the walkValues function
+ * Event types for the walkValues function
*/
exports.WalkerEvent = void 0;
(function (WalkerEvent) {
@@ -1008,7 +1069,7 @@
WalkerEvent[WalkerEvent["Leave"] = 2] = "Leave";
})(exports.WalkerEvent || (exports.WalkerEvent = {}));
/**
- * walk ast nodes
+ * Walk ast nodes
* @param node initial node
* @param filter control the walk process
* @param reverse walk in reverse order
@@ -1125,7 +1186,7 @@
}
}
/**
- * walk ast node value tokens
+ * Walk ast node value tokens
* @param values
* @param root
* @param filter
@@ -1217,7 +1278,6 @@
function* () {
// @ts-expect-error
let parent = map.get(node);
- console.debug({ parent });
while (parent != null) {
yield parent;
parent = map.get(parent);
@@ -1326,29 +1386,6 @@
}
}
- function dasherize(value) {
- return value.replace(/([A-Z])/g, (all, one) => `-${one.toLowerCase()}`);
- }
- function camelize(value) {
- return value.replace(/-([a-z])/g, (all, one) => one.toUpperCase());
- }
- function equalsIgnoreCase(a, b) {
- if (a.length !== b.length)
- return false;
- for (let i = 0; i < a.length; i++) {
- let ca = a.charCodeAt(i);
- let cb = b.charCodeAt(i);
- // Normalize A-Z to a-z
- if (ca >= 65 && ca <= 90)
- ca += 32;
- if (cb >= 65 && cb <= 90)
- cb += 32;
- if (ca !== cb)
- return false;
- }
- return true;
- }
-
var declarations = {
"-ms-accelerator": {
syntax: "false | true"
@@ -2367,6 +2404,9 @@
"forced-color-adjust": {
syntax: "auto | none | preserve-parent-color"
},
+ "frame-sizing": {
+ syntax: "auto | content-width | content-height | content-block-size | content-inline-size"
+ },
gap: {
syntax: "<'row-gap'> <'column-gap'>?"
},
@@ -3839,7 +3879,7 @@
syntax: "acos( )"
},
"alpha-value": {
- syntax: " | "
+ syntax: " | | none"
},
"an+b": {
syntax: "odd | even | | | '+'?† n | -n | | '+'?† | | | '+'?† n | -n | | '+'?† n- | -n- | ['+' | '-'] | '+'?† n ['+' | '-'] | -n ['+' | '-'] "
@@ -4001,7 +4041,7 @@
syntax: "in [ | ? | ]"
},
"color-mix()": {
- syntax: "color-mix( , [ && ? ]#{2} )"
+ syntax: "color-mix( ,? [ && ? ]#)"
},
"color-stop": {
syntax: " | "
@@ -4277,10 +4317,10 @@
syntax: "[ historical-ligatures | no-historical-ligatures ]"
},
"hsl()": {
- syntax: "hsl( , , , ? ) | hsl( [ | none ] [ | | none ] [ | | none ] [ / [ | none ] ]? )"
+ syntax: "hsl( , , , ? ) | hsl( [ | none ] [ | | none ] [ | | none ] [ / [ | none ] ]? ) | hsl(from [ | none | h | s | l ] {2} [ / [ | none] ]? )"
},
"hsla()": {
- syntax: "hsla( , , , ? ) | hsla( [ | none ] [ | | none ] [ | | none ] [ / [ | none ] ]? )"
+ syntax: "hsla( , , , ? ) | hsla( [ | none ] [ | | none ] [ | | none ] [ / [ | none ] ]? ) | hsla(from < [ | none | h | s | l ] {2} [ / [ | none ] ]? )"
},
hue: {
syntax: " | "
@@ -4292,7 +4332,7 @@
syntax: "hue-rotate( [ | ]? )"
},
"hwb()": {
- syntax: "hwb( [ | none ] [ | | none ] [ | | none ] [ / [ | none ] ]? )"
+ syntax: "hwb( [ | none ] [ | | none ] [ | | none ] [ / [ | none ] ]? ) | hwb(from [ | none | h | w | b ] {2} [ / [ | none] ]? )"
},
"hypot()": {
syntax: "hypot( # )"
@@ -4340,7 +4380,7 @@
syntax: " | "
},
"lab()": {
- syntax: "lab( [ | | none] [ | | none] [ | | none] [ / [ | none] ]? )"
+ syntax: "lab( [ | | none] [ | | none] [ | | none] [ / [ | none] ]? ) | lab(from {3} [ / [ | none ] ]? )"
},
"layer()": {
syntax: "layer( )"
@@ -4349,7 +4389,7 @@
syntax: " [ '.' ]*"
},
"lch()": {
- syntax: "lch( [ | | none] [ | | none] [ | none] [ / [ | none] ]? )"
+ syntax: "lch( [ | | none] [ | | none] [ | none] [ / [ | none] ]? ) | lch(from {2} [ | none | l | c | h ] [ / [ | none ] ]? )"
},
"leader()": {
syntax: "leader( )"
@@ -4514,10 +4554,10 @@
syntax: " | | "
},
"oklab()": {
- syntax: "oklab( [ | | none] [ | | none] [ | | none] [ / [ | none] ]? )"
+ syntax: "oklab( [ | | none] [ | | none] [ | | none] [ / [ | none] ]? ) | oklab(from {3} [ / [ | none ] ]? )"
},
"oklch()": {
- syntax: "oklch( [ from ]? [ | | none ] [ | | none ] [ | none ] [ / [ | none ] ]? )"
+ syntax: "oklch( [ | | none] [ | | none] [ | none] [ / [ | none] ]? ) | oklch(from {2} [ | none | l | c | h ] [ / [ | none ] ]? )"
},
"opacity()": {
syntax: "opacity( [ | ]? )"
@@ -4667,10 +4707,10 @@
syntax: "reversed( )"
},
"rgb()": {
- syntax: "rgb( #{3} , ? ) | rgb( #{3} , ? ) | rgb( [ | | none ]{3} [ / [ | none ] ]? )"
+ syntax: "rgb( #{3} , ? ) | rgb( #{3} , ? ) | rgb( [ | | none ]{3} [ / [ | none ] ]? ) | rgb(from {3} [ / [ | none] ]? )"
},
"rgba()": {
- syntax: "rgba( #{3} , ? ) | rgba( #{3} , ? ) | rgba( [ | | none ]{3} [ / [ | none ] ]? )"
+ syntax: "rgba( #{3} , ? ) | rgba( #{3} , ? ) | rgba( [ | | none ]{3} [ / [ | none ] ]? ) | rgba(from {3} [ / [ | none ] ]? )"
},
"rotate()": {
syntax: "rotate( [ | ] )"
@@ -4844,7 +4884,7 @@
syntax: "( )"
},
"supports-feature": {
- syntax: " | | | | | | "
+ syntax: " | "
},
"supports-in-parens": {
syntax: "( ) | | "
@@ -4982,7 +5022,7 @@
syntax: "-moz-repeating-radial-gradient( <-legacy-radial-gradient-arguments> ) | -webkit-repeating-radial-gradient( <-legacy-radial-gradient-arguments> ) | -o-repeating-radial-gradient( <-legacy-radial-gradient-arguments> )"
},
"-legacy-radial-gradient-arguments": {
- syntax: "[ , ]? [ [ [ <-legacy-radial-gradient-shape> || <-legacy-radial-gradient-size> ] | [ | ]{2} ] , ]? "
+ syntax: " ? , [ [ <-legacy-radial-gradient-shape> || <-legacy-radial-gradient-size> ] | [ | ]{2} ]? , "
},
"-legacy-radial-gradient-size": {
syntax: "closest-side | closest-corner | farthest-side | farthest-corner | contain | cover"
@@ -5185,6 +5225,21 @@
"syntax-string": {
syntax: ""
},
+ "relative-hwb-component": {
+ syntax: " | | none | h | w | b | alpha"
+ },
+ "relative-hsl-component": {
+ syntax: "