Creating a Custom PostCSS px-to-viewport Plugin for Vite to Achieve Responsive Design
This article explains how to create a custom PostCSS plugin for Vite that converts pixel units to viewport width units, covering meta tag setup, plugin configuration, TypeScript implementation, handling multi‑value properties, and troubleshooting conversion issues.
The article introduces a method for using the CSS vw unit to achieve responsive layouts, noting that 1vw equals 1% of the viewport width (e.g., a 375 px design translates to 3.75 px per vw).
1. Set the viewport meta tag to ensure the browser scales correctly:
<meta name="viewport" content="width=device-width, initial-scale=1.0">2. Add a custom PostCSS plugin by creating a plugins folder in the project root and adding two TypeScript files: postcss-px-to-viewport.ts and types.ts .
postcss-px-to-viewport.ts implements the conversion logic. It reads the design width (default 375 px), parses CSS declarations, and replaces any px values with the calculated vw value. The plugin also handles decimal numbers and multi‑value properties such as padding: 0 20px 20px .
/** 编写 postcss 插件 vite内置了postCss 无需安装 */
import { Plugin } from 'postcss';
import { type Options } from './types';
const Option = { viewPortWidth: 375 };
const transfromValue = (originalString: string, searchString: string, replaceWith: string) => {
if (!originalString.includes(searchString)) return originalString;
const escapedSearchString = searchString.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const regex = new RegExp(escapedSearchString, 'g');
return originalString.replace(regex, replaceWith);
};
export const PostCssPxToViewport = (options: Options = Option): Plugin => {
const opt = Object.assign({}, Option, options);
return {
postcssPlugin: 'postcss-px-to-viewport',
Declaration(node) {
const value = node.value;
if (!value.includes('px')) return;
if (value.endsWith('px') && !value.includes(' ')) {
const num = parseFloat(value);
if (!Number.isNaN(num)) node.value = `${((num / opt.viewPortWidth) * 100).toFixed(3)}vw`;
return;
}
let str = value;
const matches = str.match(/(\d+(\.\d+)?)\s*px/g) || [];
matches.forEach(item => {
const num = parseFloat(item);
const replaceValue = `${((num / opt.viewPortWidth) * 100).toFixed(3)}vw`;
str = transfromValue(str, item, replaceValue);
});
if (!Number.isNaN(str as any)) node.value = str;
}
};
};types.ts defines the optional configuration interface:
export type Options = {
viewPortWidth?: number;
};3. Register the plugin in tsconfig.node.json (add the plugins folder) and then reference it in vite.config.ts . Screenshots in the original article illustrate these configuration steps.
4. Debugging conversion issues – the author discovered that multi‑value properties like padding: 0 20px 20px were not fully converted because the original implementation only handled single values. By logging the node values and enhancing the logic with a regular expression that matches all px occurrences, the problem was solved.
The final solution adds a helper function transfromValue that safely replaces each matched px token with its vw counterpart, ensuring properties such as padding , border , and transformY(-14px) are correctly transformed.
5. Result – after applying the updated plugin, screenshots show that both single and multiple px values are accurately converted to vw , confirming the plugin works as intended for responsive design in a Vite‑based Vue 3 project.
Conclusion – the author, primarily an RN developer, documents the steps, pitfalls, and final working code to help others integrate a pixel‑to‑viewport conversion workflow into their frontend projects.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.