Skip to content

postcss-pxtrans

PostCSS unit conversion plugin (px → rpx/rem/vw/px) for multi-platform responsive styling. It is compatible with the legacy postcss-pxtransform option style, and supports directive comments (#ifdef/#ifndef) and RN eject.

If you need multi-rule composition, custom preset authoring, or to mix px/rem/rpx/vw/vh conversions in one plugin, prefer postcss-rule-unit-converter. Keep postcss-pxtrans when you specifically need its platform presets and directive comment workflow. For side-by-side migration examples, see postcss-rule-unit-converter migration guide.

English | 简体中文

Install

bash
pnpm add -D postcss-pxtrans

Quick start

ts
import postcss from 'postcss'
import pxTransform from 'postcss-pxtrans'

const result = await postcss([
  pxTransform({
    platform: 'h5',
    designWidth: 640,
  }),
]).process('h1 { margin: 0 0 20px; font-size: 32px; }', { from: undefined })

console.log(result.css)
// h1 { margin: 0 0 0.585rem; font-size: 0.936rem; }

Presets (platform)

platform is a preset collection that determines the default targetUnit, which units will be transformed, and how rootValue is computed. You can still override preset behavior via targetUnit, rootValue, deviceRatio, etc.

weapp

By default, converts px to rpx, and does not transform rpx.

ts
pxTransform({ platform: 'weapp', designWidth: 750 })
// h1 { margin: 20px; } -> h1 { margin: 20rpx; }

You can set targetUnit (rpx | rem | px | vw | vmin):

ts
pxTransform({ platform: 'weapp', designWidth: 750, targetUnit: 'rem' })
// 20px -> 0.5rem

Preset defaults:

  • targetUnit: rpx
  • rootValue: 1 / deviceRatio[designWidth]

h5

By default, converts px to rem, and also transforms rpx (useful when migrating MiniProgram styles to H5).

ts
pxTransform({ platform: 'h5', designWidth: 640 })
// 20px -> 0.585rem

You can set targetUnit to px | vw | vmin | rem:

ts
pxTransform({ platform: 'h5', designWidth: 640, targetUnit: 'vw' })
// 320px -> 50vw

Preset defaults (depends on targetUnit):

  • targetUnit: 'rem' -> rootValue = (baseFontSize / deviceRatio[designWidth]) * 2
  • targetUnit: 'vw' | 'vmin' -> rootValue = designWidth / 100
  • targetUnit: 'px' -> rootValue = (1 / deviceRatio[designWidth]) * 2

rn

Converts to px directly, scaled by device ratio.

ts
pxTransform({
  platform: 'rn',
  designWidth: 750,
  deviceRatio: { 640: 2.34 / 2, 750: 1, 828: 1.81 / 2 },
})
// 100px -> 50px

Preset defaults:

  • targetUnit: px
  • rootValue: (1 / deviceRatio[designWidth]) * 2

quickapp

Keeps px unchanged (1:1 output).

ts
pxTransform({ platform: 'quickapp', designWidth: 750 })
// 100px -> 100px

Preset defaults:

  • targetUnit: px
  • rootValue: 1

harmony

Key rules:

  • Normal px values are scaled and kept as px
  • Upper/mixed case units PX/Px/pX are converted to ch
  • With onePxTransform: false, 1px still becomes ch
  • If selectorBlackList matches, it still goes through the ch path
ts
pxTransform({
  platform: 'harmony',
  designWidth: 640,
  deviceRatio: { 640: 2.34 / 2, 750: 1, 828: 1.81 / 2 },
})
// 100PX -> 100ch

Preset defaults:

  • targetUnit: px
  • rootValue: 1 / deviceRatio[designWidth]

Directive comments (platform conditions / disable)

Directive handling is provided by createDirectivePlugin, and should be used together with the main plugin:

ts
import postcss from 'postcss'
import pxTransform, { createDirectivePlugin } from 'postcss-pxtrans'

const result = await postcss([
  createDirectivePlugin({ platform: 'h5' }),
  pxTransform({ platform: 'h5', designWidth: 640 }),
]).process(css, { from: 'input.css' })

Supported directives:

css
/*postcss-pxtrans disable*/
/*  #ifdef  h5  */
/*  #ifndef  weapp  */
/*  #endif  */

RN eject:

css
/*postcss-pxtrans rn eject enable*/
.a {
  width: 10px;
}
/*postcss-pxtrans rn eject disable*/

Options

Keeps naming compatible with postcss-pxtransform. Some legacy keys still work (e.g. root_value, unit_precision).

  • platform: weapp | h5 | rn | quickapp | harmony preset, default weapp
  • designWidth: number | (input) => number, default 750
  • targetUnit: rpx | rem | px | vw | vmin (platform-dependent)
  • deviceRatio: Record<number, number> mapping by design width
  • rootValue: number | (input, m, value) => number custom base
  • baseFontSize: number, used for rem/h5 calculations
  • minRootSize: number, fallback when baseFontSize is not provided
  • methods: ['platform', 'size'], can disable directive handling or size conversion
  • unitPrecision: number, decimal precision
  • selectorBlackList: (string | RegExp)[], skip if matched (Harmony still converts to ch)
  • propList: string[], property allow/deny list (wildcards and negation supported, including patterns like !--wot-*-font-size)
  • replace: boolean, when false it appends a new declaration
  • mediaQuery: boolean, convert px in @media
  • minPixelValue: number, values lower than this are not converted (Harmony still converts to ch)
  • onePxTransform: boolean, whether to convert 1px (Harmony still converts PX to ch)
  • exclude: (filePath?: string) => boolean, skip the whole file when matched

Note: internally this plugin reuses utilities from postcss-plugin-shared for prop matching and unit regex building.

Choosing Between exclude, selectorBlackList, and propList

  • exclude: skip the whole file before any declaration is processed
  • selectorBlackList: skip matching selectors inside an otherwise processed file
  • propList: skip or include specific CSS properties inside matched selectors

Use exclude for file-level boundaries such as node_modules or generated CSS. Use selectorBlackList when a rule or component should be left untouched. Use propList when the file and selector should still be processed, but some properties such as font-size or --wot-*-font-size should be skipped.

Common recipes

Dynamic designWidth per file

ts
pxTransform({
  platform: 'h5',
  designWidth(input) {
    return input.file?.includes('nutui') ? 375 : 750
  },
})

Custom rootValue

ts
pxTransform({
  platform: 'h5',
  designWidth: 750,
  rootValue: 10,
})

Transform only specific properties

ts
pxTransform({
  platform: 'h5',
  designWidth: 640,
  propList: ['*font*', 'margin*', '!margin-left', '*-right', 'pad'],
})

pxTransform({
  platform: 'h5',
  designWidth: 640,
  propList: ['*', '!--wot-*', '!--wot-fs-*', '!--wot-*-font-size'],
})

Selector blacklist

ts
pxTransform({
  platform: 'h5',
  designWidth: 640,
  selectorBlackList: ['.ignore', /^body$/],
})

Keep original and append converted value

ts
pxTransform({
  platform: 'h5',
  designWidth: 640,
  replace: false,
})
// .rule { font-size: 15px; font-size: 0.43875rem }

Convert @media

ts
pxTransform({
  platform: 'h5',
  designWidth: 640,
  mediaQuery: true,
})

Skip specific files

ts
pxTransform({
  platform: 'h5',
  designWidth: 750,
  exclude(filePath) {
    return filePath === 'skip.css'
  },
})

PostCSS config example

js
// postcss.config.cjs
const pxTransform = require('postcss-pxtrans')

module.exports = {
  plugins: [
    pxTransform({ platform: 'weapp', designWidth: 750 }),
  ],
}

If you need directive comments, also include createDirectivePlugin before the main plugin:

js
const { createDirectivePlugin } = require('postcss-pxtrans')

module.exports = {
  plugins: [
    createDirectivePlugin({ platform: 'h5' }),
    pxTransform({ platform: 'h5', designWidth: 640 }),
  ],
}

Built for CSS that moves between screens.