A PostCSS plugin to define, transform, and manage custom or existing CSS units with ease.
Install package:
pnpm add -D postcss-better-units
Note: You also need the postcss
package installed.
Configure PostCSS
to use plugin. For example with postcss.congig.ts
file:
import type { Config } from 'postcss-load-config'
import betterUnits from 'postcss-better-units'
export default {
plugins: [
betterUnits([{
fromUnit: 'rpx',
transform: value => `${value / 16}rem`,
}]),
],
} satisfies Config
That's it! The configuration above will transform rpx
units to rem
in a 1/16 ratio:
div {
width: 16rpx;
height: 5rpx;
}
/* becomes */
div {
width: 1rem;
height: 0.3125rpx;
}
The plugin accepts an array of objects. Each object describes a unit transformation:
Property | Type | Description |
---|---|---|
fromUnit * |
string |
Unit to transform from. |
transform * |
(number) => string |
Transformer function. |
toUnit |
string |
Unit to transform into. This optional shorthand may be used instead of transform . |
toCssVar |
--${string} |
CSS variable to transform into. This optional shorthand may be used instead of transform |
preserve |
boolean , 'before' , 'after' |
Whether the original value should be preserved. true means 'after' . |
exclude |
RegExp , (string, Declaration) => boolean |
RegExp or function to exclude declarations from transfomation. |
Note: One (and only one) of transform
, toUnit
or toCssVar
should be defined.
{ toUnit: '<TO_UNIT>' }
// equivalent:
{ transform: value => `${value}<TO_UNIT>` }
{ toCssVar: '<TO_CSS_VAR>' }
// equivalent:
{ transform: value => `calc(${value} * var(<TO_CSS_VAR>))` }
Here are some simple examples to board you in. You also may check our tests to find more insights.
Imagine you want to use these new awesome units like dvh
. But browser support is still limited. In such cases, fallback to less awesome units like vh
may be needed:
betterUnits([{
fromUnit: 'dvh',
toUnit: 'vh',
preserve: 'after',
}])
main {
min-height: 100dvh;
}
/* becomes */
main {
min-height: 100vh;
min-height: 100dvh;
}
Let's take the example above further. In some cases, a fallback isn't sufficient, and you might use a JavaScript polyfill to make these dvh
units work everywhere. Typically, polyfills set a CSS variable that can be used as the desired unit:
betterUnits([{
fromUnit: 'dvh',
toCssVar: '--dvh',
preserve: 'after',
}])
main {
min-height: 100dvh;
}
/* becomes */
main {
min-height: calc(100 * var(--dvh));
min-height: 100dvh;
}
If you are interested in such polyfill, check out this project for inspiration.
There is CSS draft for a syntax sugar called variable units
. You can use it right now:
betterUnits([{
fromUnit: '--fem',
toCssVar: '--fem',
}])
.fluid-type {
font-size: 1.2--fem;
}
/* becomes */
.fluid-type {
min-height: calc(1.2 * var(--fem));
}
Sometimes unit should be transform into expression based on this unit. In this case postcss will get stuck inside an infinite recursion. To make things work, you need to break this recursion with creative exclusion rule.
betterUnits([{
fromUnit: 'vw',
transform: value => `calc(${value}vw / var(--screen-scale))`,
exclude: /--screen-scale/,
}])
:root {
--screen-scale: 0.5;
}
main {
zoom: var(--screen-scale);
width: 100vw;
}
/* becomes */
:root {
--screen-scale: 0.5;
}
main {
zoom: var(--screen-scale);
width: calc(100vw / var(--screen-scale));
}
Feel free to request features or report bugs, even if project seems abandoned. Feedback is always appreciated.