postcss-rule-unit-converter
v0.2.2
Published
Convert CSS units with composable PostCSS rules and built-in presets.
Maintainers
Readme
postcss-rule-unit-converter
基于规则的通用 PostCSS 单位转换插件。
- 场景示例文档:COOKBOOK.zh-CN.md
- API 文档:API.zh-CN.md
- 迁移文档:MIGRATION.zh-CN.md
- Preset 编写指南:PRESET_AUTHORING.zh-CN.md
特性
- 一个插件统一处理
rpx、px、rem、vw、vh以及自定义单位 - 按规则顺序匹配转换
- 内置常见单位的双向 preset
- 复用仓库里现有的属性过滤、选择器黑名单、文件排除等能力
- 同时支持单个 preset 和按场景封装好的 preset group
心智模型
- 每条规则负责匹配一种源单位并生成一个新值。
- 规则按顺序执行。
- 如果多条规则都能命中同一个源单位,先写的那条优先。
- 需要组合多组 preset 和自定义规则时,用
composeRules(...)明确最终顺序。
用法
import postcss from 'postcss'
import unitConverter, { composeRules, presets } from 'postcss-rule-unit-converter'
const result = await postcss([
unitConverter({
rules: composeRules(
presets.remToViewport({ viewportWidth: 375 }),
presets.pxToRem({ rootValue: 16 }),
),
}),
]).process('.title{font-size:1rem;margin:16px}', { from: undefined })分组 Preset
import postcss from 'postcss'
import unitConverter, { composeRules, presets } from 'postcss-rule-unit-converter'
const result = await postcss([
unitConverter({
rules: composeRules(
presets.rpxPresetGroup({
ratio: 2,
rootValue: 16,
viewportWidth: 375,
viewportHeight: 667,
}),
{
from: 'em',
to: 'rpx',
factor: 32,
},
),
}),
]).process('.demo{font-size:32rpx;left:10vw}', { from: undefined })内置的分组 preset:
presets.rpxPresetGroup()把px/rem/vw/vh归一成rpxpresets.pxPresetGroup()把rem/rpx/vw/vh归一成pxpresets.viewportPresetGroup()把px/rem/rpx归一成单一目标轴的vw或vhpresets.webPresetGroup()提供一组偏 Web 场景的px/rem/vw/vh/rpx常用规则
从 postcss-units-to-px 迁移
presets.unitsToPx() 支持和 postcss-units-to-px 同形状的 unitMap:
import postcss from 'postcss'
import unitConverter, { presets } from 'postcss-rule-unit-converter'
const result = await postcss([
unitConverter({
propList: ['*'],
rules: presets.unitsToPx({
unitMap: {
rem: 16,
vw: false,
foo: null,
},
transform(value, unit, context) {
return unit === 'foo' && context.prop === 'margin' ? value * 10 : undefined
},
}),
}),
]).process('.demo{font-size:1rem;width:1vw;margin:2foo}', { from: undefined })对象形式的 unitMap 会覆盖并合并默认的 rem/em/vw/vh/vmin/vmax/rpx
映射。Map 和数组形式会保留用户传入顺序,并且不合并默认值。单个单位规则为
false 时跳过该单位;为 null 时走 transform(value, unit, context) 兜底。
更多示例
import postcss from 'postcss'
import unitConverter, { presets } from 'postcss-rule-unit-converter'
const result = await postcss([
unitConverter({
rules: [
presets.rpxToPx(),
presets.pxToRpx(),
presets.rpxToRem({ rootValue: 16 }),
presets.rpxToVw({ viewportWidth: 375 }),
presets.rpxToVh({ viewportHeight: 667 }),
presets.vwToPx({ viewportWidth: 375 }),
presets.vhToPx({ viewportHeight: 667 }),
],
}),
]).process('.demo{font-size:32rpx;width:10vw;height:10vh}', { from: undefined })自定义 Preset
单条 preset,自带导出的类型:
import type { PresetFactory, RemBasedPresetOptions } from 'postcss-rule-unit-converter'
import postcss from 'postcss'
import unitConverter, {
definePreset
} from 'postcss-rule-unit-converter'
const remToDp: PresetFactory<RemBasedPresetOptions> = definePreset((options = {}) => {
const { rootValue = 16, minValue, to = 'dp' } = options
return {
from: 'rem',
to,
minValue,
transform: (value, context) => {
const resolvedRootValue = typeof rootValue === 'function'
? rootValue(context.input)
: rootValue
return value * resolvedRootValue
},
}
})
const result = await postcss([
unitConverter({
rules: [remToDp({ rootValue: 20 })],
}),
]).process('.demo{font-size:1rem}', { from: undefined })分组 preset,自带导出的类型:
import type { PresetGroupFactory, RemBasedPresetOptions } from 'postcss-rule-unit-converter'
import postcss from 'postcss'
import unitConverter, {
definePresetGroup
} from 'postcss-rule-unit-converter'
type MyPresetOptions = RemBasedPresetOptions & {
ratio?: number
}
const mobilePresetGroup: PresetGroupFactory<MyPresetOptions> = definePresetGroup((options = {}) => {
const { rootValue = 16, ratio = 2 } = options
return [
{
from: 'rem',
to: 'rpx',
transform: (value, context) => {
const resolvedRootValue = typeof rootValue === 'function'
? rootValue(context.input)
: rootValue
return value * resolvedRootValue * ratio
},
},
{
from: 'px',
to: 'rpx',
factor: ratio,
},
]
})
const result = await postcss([
unitConverter({
rules: mobilePresetGroup({ rootValue: 16, ratio: 2 }),
}),
]).process('.demo{font-size:1rem;margin:16px}', { from: undefined })自定义 transform 规则:
import postcss from 'postcss'
import unitConverter from 'postcss-rule-unit-converter'
const result = await postcss([
unitConverter({
rules: [
{
from: /^x$/,
to: 'px',
transform(value, context) {
return context.prop === 'letter-spacing' ? value * 4 : value * 8
},
},
],
}),
]).process('.demo{letter-spacing:2x;margin:2x}', { from: undefined })使用建议
- 想保留兜底声明时,用
replace: false。 - 想避免多条 preset 对同一源单位发生竞争时,用
propList把适用范围收窄。 - 项目里如果有一个主单位体系,优先使用 preset group。
- 如果你要严格控制命中顺序,优先直接写显式规则数组。
- 使用
presets.viewportPresetGroup()时,建议显式指定viewportUnit: 'vw'或viewportUnit: 'vh'。 - 在业务里沉淀可复用 preset 时,优先使用
definePreset(...)和definePresetGroup(...)。 RuleContext.fromUnit是用于规则匹配的源单位;rawUnit保留原始大小写;rawValue是Number(...)前的数字文本;match是完整匹配片段。- 字符串单位 matcher 会归一化为小写。默认正则下
'px'匹配小写px;需要匹配PX/Px时使用/^px$/i或自定义unitRegex。 - 默认正则会跳过字符串、
url(...)和var(...)。自定义unitRegex会完全替换默认正则;如果仍要跳过这些内容,需要在自定义正则里保留跳过分支。 - 如果你想直接抄场景配置,优先看 cookbook。
Presets
自定义 preset 辅助类型:
definePreset()definePresetGroup()type PresetFactory<TOptions>type PresetGroupFactory<TOptions>presets.remToPx()presets.remToRpx()presets.remToRpxRatio()presets.remToRpxByRatio()presets.remToResponsivePixel()presets.remToViewport()presets.remToVw()presets.remToVh()presets.pxToRem()presets.pxToViewport()presets.pxToVw()presets.pxToVh()presets.pxToRpx()presets.rpxToPx()presets.rpxToRem()presets.rpxToVw()presets.rpxToVh()presets.vwToPx()presets.vhToPx()presets.vwToRem()presets.vhToRem()presets.vwToRpx()presets.vhToRpx()presets.pxPresetGroup()presets.rpxPresetGroup()presets.viewportPresetGroup()presets.webPresetGroup()presets.unitsToPx()
