Détail du package

@tbela99/css-parser

tbela99607MIT OR LGPL-3.01.3.4

CSS parser, minifier and validator for node and the browser

parser, css, css-parser, node

readme

playground npm npm cov Doc NPM Downloads bundle size

css-parser

CSS parser, minifier and validator for node and the browser

Installation

From npm

$ npm install @tbela99/css-parser

from jsr

$ deno add @tbela99/css-parser

Features

  • no dependency
  • CSS validation based upon mdn-data
  • fault-tolerant parser, will try to fix invalid tokens according to the CSS syntax module 3 recommendations.
  • fast and efficient minification without unsafe transforms, see benchmark
  • minify colors: color(), lab(), lch(), oklab(), oklch(), color-mix(), light-dark(), system colors and relative color
  • generate nested css rules
  • convert nested css rules to legacy syntax
  • convert colors to any supported color format
  • generate sourcemap
  • compute css shorthands. see supported properties list below
  • minify css transform functions
  • evaluate math functions: calc(), clamp(), min(), max(), etc.
  • inline css variables
  • remove duplicate properties
  • flatten @import rules
  • experimental CSS prefix removal

Online documentation

See the full documentation at the CSS Parser documentation site

Playground

Try it online

Exports

There are several ways to import the library into your application.

Node exports

import as a module


import {transform} from '@tbela99/css-parser';

// ...

Deno exports

import as a module


import {transform} from '@tbela99/css-parser';

// ...

import as a CommonJS module


const {transform} = require('@tbela99/css-parser/cjs');

// ...

Web export

Programmatic import


import {transform} from '@tbela99/css-parser/web';

// ...

Javascript module from cdn


<script type="module">

    import {transform} from 'https://esm.sh/@tbela99/css-parser@1.3.4/web';

    const css = `
    .s {

    background: color-mix(in hsl, color(display-p3 0 1 0) 80%, yellow);
}
    `;

    console.debug(await transform(css).then(r => r.code));

</script>

Javascript module


<script src="dist/web.js" type="module"></script>

Javascript umd module from cdn


<script src="https://unpkg.com/@tbela99/css-parser@1.3.4/dist/index-umd-web.js"></script>
<script>

    (async () => {

        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;
}
    `;

        console.debug(await CSSParser.transform(css, {beautify: true, convertColor: CSSParser.ColorType.OKLCH}).then(r => r.code));
    })();

</script>

Transform

Parse and render css in a single pass.

Usage


transform(css: string | ReadableStream<string>, transformOptions: TransformOptions = {}): TransformResult
parse(css: string | ReadableStream<string>, parseOptions: ParseOptions = {}): ParseResult;
render(ast: AstNode, renderOptions: RenderOptions = {}): RenderResult;

Example


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

import {transform} from "../src/node";
import {Readable} from "node:stream";
import type {TransformResult} from '../src/@types/index.d.ts';

const readableStream: ReadableStream<string> = Readable.toWeb(process.stdin) as ReadableStream<string>;
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 pass. 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.
  • 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 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


parse(css, parseOptions = {})

Example


const {ast, errors, stats} = await parse(css);

Rendering

Usage

render(ast, RenderOptions = {});

Examples

Rendering ast

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}}

Convert colors

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

.hsl {
    color: color(srgb .7019607843137254 .13333333333333333 .13333333333333333/50%)
}

Merge similar rules

CSS


.clear {
    width: 0;
    height: 0;
    color: transparent;
}

.clearfix:before {

    height: 0;
    width: 0;
}

import {transform} from '@tbela99/css-parser';

const result = await transform(css);

Result

.clear, .clearfix:before {
    height: 0;
    width: 0
}

.clear {
    color: #0000
}

Automatic CSS Nesting

CSS

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

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


#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

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);
.s:is([type=text],[type=text i],[type=text s],[type=text i]+b,:focus) {
    --animate-duration: 1s
}

with validation disabled

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);
.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

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

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


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;
}

Calc() resolution


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

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


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

.foo-bar {
    width: 12px;
    height: 25%
}

CSS variable inlining and relative color


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

._19_u :focus {
    color: navy
}

CSS variable inlining and relative color


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

.overlay {
    background: #0c6464
}

Node Walker


import {walk} from '@tbela99/css-parser';

for (const {node, parent, root} of walk(ast)) {

    // do something
}

AST

Comment

  • typ: number
  • val: string, the comment

Declaration

  • typ: number
  • nam: string, declaration name
  • val: array of tokens

Rule

  • typ: number
  • sel: string, css selector
  • chi: array of children

AtRule

  • typ: number
  • nam: string. AtRule name
  • val: rule prelude

AtRuleStyleSheet

  • typ: number
  • chi: array of children

KeyFrameRule

  • typ: number
  • sel: string, css selector
  • chi: array of children

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

Computed shorthands properties

  • [ ] ~all~
  • [x] animation
  • [x] background
  • [x] border
  • [ ] border-block-end
  • [ ] border-block-start
  • [x] border-bottom
  • [x] border-color
  • [ ] border-image
  • [ ] border-inline-end
  • [ ] border-inline-start
  • [x] border-left
  • [x] border-radius
  • [x] border-right
  • [x] border-style
  • [x] border-top
  • [x] border-width
  • [x] column-rule
  • [x] columns
  • [x] container
  • [ ] contain-intrinsic-size
  • [x] flex
  • [x] flex-flow
  • [x] font
  • [ ] font-synthesis
  • [ ] font-variant
  • [x] gap
  • [ ] grid
  • [ ] grid-area
  • [ ] grid-column
  • [ ] grid-row
  • [ ] grid-template
  • [x] inset
  • [x] list-style
  • [x] margin
  • [ ] mask
  • [ ] offset
  • [x] outline
  • [x] overflow
  • [x] padding
  • [ ] place-content
  • [ ] place-items
  • [ ] place-self
  • [ ] scroll-margin
  • [ ] scroll-padding
  • [ ] scroll-timeline
  • [x] text-decoration
  • [x] text-emphasis
  • [x] transition

Performance

  • [x] flatten @import

Node Transformation

Ast can be transformed using node visitors

Exemple 1: Declaration

the visitor is called for any declaration encountered


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))}

Exemple 2: Declaration

the visitor is called only on 'height' declarations


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: [
                            <LengthToken>{
                                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}

Exemple 3: At-Rule

the visitor is called on any at-rule


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)}

Exemple 4: At-Rule

the visitor is called only for at-rule media


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)}

Exemple 5: Rule

the visitor is called on any Rule


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)}

Exemple 6: Rule

Adding declarations to any rule


import {AstRule, parseDeclarations, ParserOptions, transform} from '@tbela99/css-parser';

const options: ParserOptions = {

    removeEmpty: false,
    visitor: {

        Rule: async (node: AstRule): Promise<AstRule | null> => {

            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}

changelog

Changelog

v1.3.4

  • [x] make streaming optional #109
  • [x] patch at-rule syntax for @font-feature-value #110
  • [x] support percentage in transform() and scale() #111
  • [x] allow arrays in visitor definition #112

v1.3.3

  • [x] relative color computation bug #105
  • [x] allow duplicated declarations of whitelisted properties #106

v1.3.2

  • [x] add missing return type

v1.3.1

  • [x] generate documentation
  • [x] parse input from readable stream
  • [x] add parseFile() and transformFile()
  • v1.3.0

  • [x] numerical and dimension tokens use numbers instead of string

  • [x] fix bug when inlineCssVariables is disabled and computeCalcExpression is enabled

V1.2.0

  • [x] convert color to any supported color space #94
  • [x] dead code removal #93
  • [x] validation syntax update #92

v1.1.1

  • [x] fix bug when css nesting is disabled #89
  • [x] validation rules update #87

v1.1.0

  • [x] inline sourcemap
  • [x] CSS validation using mdn-data
  • [x] prefix removal now remove prefixes from all nodes. prefixed linear gradients are not supported

v1.0.0

  • [x] current color parse error when used in color functions
  • [x] minification : CSS transform module level 2
    • [x] translate
    • [x] scale
    • [x] rotate
    • [x] skew
    • [x] perspective
    • [x] matrix
    • [x] matrix3d
  • [x] keyframes
    • [x] remove consecutive keyframes with the same name
    • [x] reduce keyframe selector 'from' to '0%'
    • [x] reduce keyframe selector '100%' to 'to'
    • [x] remove keyframe selector ignored declarations
    • [x] merge keyframe rules and declarations
  • [x] trim extra space in declaration value 'url() 15%' -> 'url()15%', '1% !important' -> '1%!important'
  • [x] allow empty value for css variable declaration '--color: ;'
  • [x] change generate nesting rule default to true

v0.9.1

  • [x] minification passes #66
  • [x] nesting selector cannot match pseudo element #67

v0.9.0

validation

  • [x] validate invalid pseudo classes
  • [x] rewrite selector validation
  • [x] lenient mode that preserves
    • [x] unknown at-rules
    • [x] unknown declarations
    • [x] unknown pseudo classes

media query level 5

  • [x] at-rule custom-media
  • [x] at-rule when-else custom media
  • [x] at-rule charset validation
  • [x] media query error handling
  • [x] at-rule container
  • [ ] expand at-rule custom-media
  • [ ] expand at-rule when-else

selector validation

  • [ ] pseudo class arguments validation
  • [ ] pseudo class validation

declaration validation

  • [ ] validate declaration

error validation

  • [ ] when a parent is marked as invalid node, do not parse or validate descendant nodes

v0.8.0

  • [x] validate selectors using mdn data
  • [x] at-rules prefix removal
  • [x] at rules validation
    • [x] at-rule prelude
    • [x] at-rule body
    • [x] keyframe validation
    • [ ] :not() does not accept pseudo classes
    • [ ] do not validate declarations in @supports
  • [ ] declarations validation
  • [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() #49
  • [x] incorrectly parse compound selector #51

v0.7.1

  • [x] fix nesting rules expansion #45

v0.7.0

  • [x] fix merging rules
  • [ ] experimental CSS prefix removal
    • [x] declaration name
    • [ ] declaration value
    • [ ] exclude -webkit-* gradients
  • [x] css selector validation
    • [x] pseudo element
    • [x] partial pseudo class validation. does not validate parameters
    • [x] attribute selector
    • [x] combinator
    • [x] simple selector
    • [x] nested selector
    • [x] strict vs permissive validation: allow unknown items such as pseudo classes
      • [x] allow unknown pseudo classes
      • [x] allow unknown attribute selectors
  • [x] strip universal selector when possible

v0.6.0

  • [x] light-dark() color
  • [x] system color

V0.5.4

  • [x] incorrectly expand css nesting rules

v0.5.3

  • [x] incorrectly expand css nesting rules

v0.5.1

  • [x] failed to flatten @import when using url() syntax

v0.5.0

  • [x] render node with parents
  • [x] fix relative color from xyz
  • [x] fix bug when inlineCss is true but no css variable exists
  • [x] compute more shorthands
  • [x] (web) fetch imported css files from external domains using cors

v0.4.1

no code change

v0.4.0

Parsing

  • [x] allow async node visitors
  • [x] adding declaration parsing helper async parseDeclarations(source: string): Promise<AstDeclarations[]>

CSS color level 4 & 5

  • [x] color space: srgb, srgb-linear, display-p3, prophoto-rgb, a98-rgb, rec2020, xyz, xyz-d50
  • [x] color-mix()
  • [x] color()
  • [x] relative color
  • [x] lab()
  • [x] lch()
  • [x] oklab()
  • [x] oklch()

v0.3.0

shorthands

  • [x] column-rule
  • [x] columns
  • [x] container
  • [x] flex
  • [x] flex-flow
  • [x] gap

Other

  • [x] renamed RenderOptions.colorConvert to RenderOptions.convertColor
  • [x] support none keyword in color
  • [x] css relative color syntax for rgb(), hsl() and hwb() colors https://www.w3.org/TR/css-color-5/#relative-colors
    • [x] rgb
    • [x] hex
    • [x] hsl
    • [x] hwb
    • [x] calc()
    • [x] calc() and inline var()

v0.2.0

  • [x] cancellable parser promise using abortSignal
  • [x] node visitor (callback) :
    • [x] Declaration visitor
    • [x] selector visitor
    • [x] at-rule visitor
  • [x] support mixing units with calc()

shorthands

  • [x] transition
  • [x] list-style
  • [x] text-emphasis
  • [x] animation

Minification

v0.1.0

  • [x] sourcemap generation
  • [x] reduce calc():
  • [x] inline css variables
  • [x] configure duplicate declarations removal
  • [x] configure shorthand properties computation

v0.0.1

Minification

  • [x] merge identical rules
  • [x] merge adjacent rules
  • [x] minify colors
  • [x] minify numbers and Dimensions tokens
  • [x] compute shorthand: see the list below
  • [x] remove redundant declarations
  • [x] simple shorthand properties (padding, margin, etc). must have all required properties
  • [x] complex shorthand properties (background, font, etc.). may have optional properties
  • [x] conditionally unwrap :is()
  • [x] automatic css nesting
  • [x] automatically wrap selectors using :is()
  • [x] multi-level shorthand properties ( border - [border-width, border-color, border-style, etc.]) https://developer.mozilla.org/en-US/docs/Web/CSS/Shorthand_properties
  • [x] avoid reparsing (declarations, selectors, at-rule)
  • [x] node and browser versions
  • [x] decode and replace utf-8 escape sequence

Computed shorthands

  • [x] background
  • [x] border
  • [x] border-bottom
  • [x] border-color
  • [x] border-left
  • [x] border-radius
  • [x] border-right
  • [x] border-style
  • [x] border-top
  • [x] border-width
  • [x] font
  • [x] inset
  • [x] margin
  • [x] outline
  • [x] overflow
  • [x] padding
  • [x] text-decoration

Performance

  • [x] flatten @import

Error handling

  • [x] parse bad comments / cdo comments
  • [x] parse bad string 1
  • [x] parse bad string 2
  • [x] parse empty declaration
  • [x] parse unclosed rule
  • [x] parse unclosed at-rule
  • [x] parse bad import

Testing

  • [x] node tests
  • [x] browser tests

Code Coverage

  • [x] node
  • [x] browser