postcss-calc
Version:
PostCSS plugin to reduce calc()
222 lines (169 loc) • 5.88 kB
Markdown
# PostCSS Calc [<img src="https://postcss.github.io/postcss/logo.svg" alt="PostCSS" width="90" height="90" align="right">][PostCSS]
[![NPM Version][npm-img]][npm-url]
[![Support Chat][git-img]][git-url]
[PostCSS Calc] lets you reduce `calc()` references whenever it's possible.
When multiple units are mixed together in the same expression, the `calc()`
statement is left as is, to fallback to the [W3C calc() implementation].
## Installation
```bash
npm install postcss-calc
```
## Usage
```js
// dependencies
var fs = require('fs');
var postcss = require('postcss');
var calc = require('postcss-calc');
// css to be processed
var css = fs.readFileSync('input.css', 'utf8');
// process css
var output = postcss().use(calc()).process(css).css;
```
Using this `input.css`:
```css
h1 {
font-size: calc(16px * 2);
height: calc(100px - 2em);
width: calc(2 * var(--base-width));
margin-bottom: calc(16px * 1.5);
}
```
you will get:
```css
h1 {
font-size: 32px;
height: calc(100px - 2em);
width: calc(2 * var(--base-width));
margin-bottom: 24px;
}
```
Checkout [tests] for more examples.
### Options
#### `precision` (default: `5`)
Allows you to define the precision for decimal numbers. Set it to `false` to
disable rounding.
```js
var out = postcss()
.use(calc({ precision: 10 }))
.process(css).css;
```
#### `warnWhenCannotResolve` (default: `false`)
Adds warnings when calc() are not reduced to a single value.
```js
var out = postcss()
.use(calc({ warnWhenCannotResolve: true }))
.process(css).css;
```
#### `mediaQueries` (default: `false`)
Allows calc() usage in media query parameters.
```js
var out = postcss()
.use(calc({ mediaQueries: true }))
.process(css).css;
```
Example:
```css
@media (min-width: calc(100px + 100px)) {
div {
width: 100px;
}
}
```
With `mediaQueries: true`, this becomes:
```css
@media (min-width: 200px) {
div {
width: 100px;
}
}
```
#### `selectors` (default: `false`)
Allows calc() usage as part of selectors.
```js
var out = postcss()
.use(calc({ selectors: true }))
.process(css).css;
```
Example:
```css
div:nth-child(calc(1 + 2)) {
width: 100px;
}
```
With `selectors: true`, this becomes `div:nth-child(3)`.
#### `onParseError`
Callback invoked when a `calc()` body fails to parse or simplify. Matches
[`@csstools/css-calc`][csstools-css-calc]'s shape:
```js
calc({
onParseError: (err, input) => {
throw err; // or log, route to a different channel, etc.
},
});
```
When omitted, errors are reported via PostCSS `result.warn()` so the
plugin never throws at the postcss level.
### Behavior differences from the legacy parser
The legacy [jison][jison]-generated parser was replaced by a hand-written
Pratt parser whose simplifier follows [CSS Values 4][css-values-4]. Most
inputs reduce to identical output; the differences are spec-aligned or
canonical-form decisions:
- **Strict whitespace (§10.1).** `calc(2px+3px)` is invalid CSS (binary
`+` / `-` require surrounding whitespace) and is preserved with a
warning instead of reduced.
- **Canonical operand order.** Commutative operands serialize
numeric-first, matching [`@csstools/css-calc`][csstools-css-calc]:
`calc(var(--foo) + 10px)` → `calc(10px + var(--foo))`.
- **Zero buckets are kept.** `calc(100px - (100px - 100%))` →
`calc(0px + 100%)`, not `100%` — [WPT calc-serialization-002][wpt-calc-serialization]
requires the zero term because it carries the length-percentage type.
- **Constant folding.** `calc(43 + pi)` now folds to `46.14159` (§10.7.1).
Previously `pi` / `e` stayed symbolic.
- **Reciprocal conversion.** `calc(var(--x) / 2)` becomes
`calc(var(--x) * 0.5)`. The two are mathematically equivalent;
previously the division shape was kept.
- **Distributive multiplication.** `calc(0.5 * (100vw - 10px))` becomes
`calc(50vw - 5px)`.
- **Unit case normalization.** `2PX` becomes `2px` (CSS units are case-
insensitive; lowercase is conventional).
- **Calc unwrap (§10.6).** `calc(var(--foo))` becomes `var(--foo)` — a
`calc()` containing a single value is replaced by that value.
- **Spec-style spaced operators.** `2px*var(--x)` is serialized as
`2px * var(--x)`. The tokenizer is unaffected; only output spacing
differs.
- **Division by zero / by a unit.** `calc(500px/0)` reduces to
`calc(infinity * 1px)` (§10.13) instead of throwing. Use `onParseError`
if you want validation behavior.
[css-values-4]: https://www.w3.org/TR/css-values-4/
[csstools-css-calc]: https://www.npmjs.com/package/@csstools/css-calc
[wpt-calc-serialization]: https://github.com/web-platform-tests/wpt/blob/master/css/css-values/calc-serialization-002.html
[jison]: https://github.com/zaach/jison
---
## Related PostCSS plugins
To replace the value of CSS custom properties at build time, try [PostCSS Custom Properties].
## Contributing
Work on a branch, install dev-dependencies, respect coding style & run tests
before submitting a bug fix or a feature.
```bash
git clone git@github.com:postcss/postcss-calc.git
git checkout -b patch-1
npm install
npm test
```
The normal test run uses a deterministic structural sample of the harvested
real-world corpus. Run the complete differential corpus before releases or
when changing parsing/simplification behavior:
```bash
pnpm test:corpus:full
```
## [Changelog](CHANGELOG.md)
## [License](LICENSE)
[git-img]: https://img.shields.io/badge/support-chat-blue.svg
[git-url]: https://gitter.im/postcss/postcss
[npm-img]: https://img.shields.io/npm/v/postcss-calc.svg
[npm-url]: https://www.npmjs.com/package/postcss-calc
[PostCSS]: https://github.com/postcss
[PostCSS Calc]: https://github.com/postcss/postcss-calc
[PostCSS Custom Properties]: https://github.com/postcss/postcss-custom-properties
[tests]: test/index.js
[W3C calc() implementation]: https://www.w3.org/TR/css3-values/#calc-notation