
Security News
TC39 Advances 11 Proposals for Math Precision, Binary APIs, and More
TC39 advances 11 JavaScript proposals, with two moving to Stage 4, bringing better math, binary APIs, and more features one step closer to the ECMAScript spec.
@tbela99/css-parser
Advanced tools
CSS parser and minifier for node and the browser
From npm
$ npm install @tbela99/css-parser
from jsr
$ deno add @tbela99/css-parser
Try it online
There are several ways to import the library into your application.
import as a module
import {transform} from '@tbela99/css-parser';
// ...
import as a module
import {transform} from '@tbela99/css-parser';
// ...
import as a CommonJS module
const {transform} = require('@tbela99/css-parser/cjs');
// ...
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.0.0/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/index.js" type="module"></script>
Single Javascript file
<script src="dist/index-umd-web.js"></script>
Parse and render css in a single pass.
transform(css, transformOptions: TransformOptions = {}): TransformResult
import {transform} from '@tbela99/css-parser';
const {ast, code, map, errors, stats} = await transform(css, {minify: true, resolveImport: true, cwd: 'files/css'});
Include ParseOptions and RenderOptions
Minify Options
Validation Options
Sourcemap Options
Ast Traversal Options
Urls and @import Options
Misc Options
Minify Options
Sourcemap Options
Misc Options
parse(css, parseOptions = {})
const {ast, errors, stats} = await parse(css);
render(ast, RenderOptions = {});
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}}
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
}
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
#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
}
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;
}
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)
}
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%
}
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
}
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
}
import {walk} from '@tbela99/css-parser';
for (const {node, parent, root} of walk(ast)) {
// do something
}
Ast can be transformed using node visitors
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));
}
`;
console.debug(await transform(css, options));
// .foo{transform:scale(calc(40/3))}
the visitor is called only on 'height' declarations
import {AstDeclaration, LengthToken, ParserOptions} from "../src/@types";
import {EnumToken} from "../src/lib";
import {transform} from "../src/node";
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.Length,
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))
}
`;
console.debug(await transform(css, options));
// .foo{height:calc(40px/3);width:3px}.selector{color:#0880b0}
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);
}
}
`;
console.debug(await transform(css, options));
// .foo{height:calc(40px/3)}
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 => {
return {...node, val: 'all'}
}
}
}
};
const css = `
@media screen {
.foo {
height: calc(100px * 2/ 15);
}
}
`;
console.debug(await transform(css, options));
// .foo{height:calc(40px/3)}
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 {
return {...node, sel: '.foo,.bar,.fubar'};
}
}
};
const css = `
.foo {
height: calc(100px * 2/ 15);
}
`;
console.debug(await transform(css, options));
// .foo,.bar,.fubar{height:calc(40px/3)}
Adding declarations to any rule
import {transform} from "../src/node";
import {AstRule, ParserOptions} from "../src/@types";
import {parseDeclarations} from "../src/lib";
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 {
}
`;
console.debug(await transform(css, options));
// .foo{width:3px}
Thanks to Jetbrains for sponsoring this project with a free license
v1.0.0
FAQs
CSS parser for node and the browser
The npm package @tbela99/css-parser receives a total of 55 weekly downloads. As such, @tbela99/css-parser popularity was classified as not popular.
We found that @tbela99/css-parser demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
TC39 advances 11 JavaScript proposals, with two moving to Stage 4, bringing better math, binary APIs, and more features one step closer to the ECMAScript spec.
Research
/Security News
A flawed sandbox in @nestjs/devtools-integration lets attackers run code on your machine via CSRF, leading to full Remote Code Execution (RCE).
Product
Customize license detection with Socket’s new license overlays: gain control, reduce noise, and handle edge cases with precision.