Babel plugin for convert px to rem units of styled-components
npm install @hsuna/babel-plugin-styled-components-pxtorem !NPM Downloads  
Babel plugin for convert px to rem units of styled-components
1. Use postcss-pxtorem to process all css text in template strings.
2. Add a runtime pxtorem function polyfill to process expression embedded in template strings when enable transformRuntime option.
- Requirement
- Usage
- Configuration
- Composition
- Options
- Transform Runtime
- FunctionExpression
- ArrowFunctionExpression
- MemberExpression
- ConditionalExpression
- Other Expressions
You need to install the following peerDependencies of babel-plugin-styled-components-pxtorem into your project at the same time:
``json`
{
"peerDependencies": {
"@babel/core": "^7.26.0",
"postcss": "^8.0.0"
}
}
see example
The use of React and styled-components test cases.
babel.config.js:
`js`
module.exports = {
plugins: [
['styled-components-pxtorem', { rootValue: 100, unitPrecision: 5, minPixelValue: 0, transformRuntime: false }],
],
};
or .babelrc:
`json`
{
"plugins": [
[
"styled-components-pxtorem",
{ "rootValue": 100, "unitPrecision": 5, "minPixelValue": 0, "transformRuntime": false }
]
]
}
It should be put before babel-plugin-styled-components
`json`
{
"plugins": ["styled-components-pxtorem", "styled-components"]
}
Type: Object | Null
Default:
`js`
{
rootValue: 100,
unitPrecision: 5,
selectorBlackList: [],
propList: ['*'],
replace: true,
mediaQuery: false,
minPixelValue: 0,
exclude: null,
unit: 'px',
tags: ['styled', 'css', 'createGlobalStyle', 'keyframes'],
transformRuntime: false,
}
- rootValue (Number | Function) Represents the root element font size or returns the root element font size based on the input parameterunitPrecision
- (Number) The decimal numbers to allow the REM units to grow to.propList
- (Array) The properties that can change from px to rem.
- Values need to be exact matches.
- Use wildcard to enable all properties. Example: ['']
- Use at the start or end of a word. (['position*'] will match background-position-y)!
- Use to not match a property. Example: ['*', '!letter-spacing']['', '!font']
- Combine the "not" prefix with the other prefixes. Example: selectorBlackList
- (Array) The selectors to ignore and leave as px.['body']
- If value is string, it checks to see if selector contains the string.
- will match .body-class[/^body$/]
- If value is regexp, it checks to see if the selector matches the regexp.
- will match body but not .bodyreplace
- (Boolean) Replaces rules containing rems instead of adding fallbacks.mediaQuery
- (Boolean) Allow px to be converted in media queries.minPixelValue
- (Number) Set the minimum pixel value to replace.exclude
- (String, Regexp, Function) The file path to ignore and leave as px.'exclude'
- If value is string, it checks to see if file path contains the string.
- will match \project\postcss-pxtorem\exclude\path/exclude/i
- If value is regexp, it checks to see if file path matches the regexp.
- will match \project\postcss-pxtorem\exclude\pathfunction (file) { return file.indexOf('exclude') !== -1; }
- If value is function, you can use exclude function to return a true and the file will be ignored.
- the callback will pass the file path as a parameter, it should returns a Boolean result.
- unit
- (String) Set the default unit to convert, default is px.tags
- (Array) styled-components template literal taggedtransformRuntime
- (Boolean) since 1.1.0,enable transformation of all expressions that embedded in template strings
Simple version of the formula:
`js
const input = '32px'; // the value in css text
const pixels = parseFloat(input);
if (Math.abs(pixels) < minPixelValue) {
return input;
}
const fixedVal = toFixed(pixels / rootValue, unitPrecision);
return ${fixedVal}rem;`
Remaining options are consistent with postcss-pxtorem.
If enabled transformRuntime option, all supported expressions embedded in template strings are processed as follows:
Note: Only expression that end with px will be processed.
source code:
`javascript
import styled from 'styled-components';
export const FunctionExpression = styled.button
height: ${function(props) {
return props.height;
}}px;;`
compiled:
`javascript
import styled from 'styled-components';
export const FunctionExpression = styled.button
height: ${(...args) =>
_pxtorem(function(props) {
return props.height;
}, ...args)};;
function _pxtorem(input, ...args) {
if (typeof input === 'function') return _pxtorem(input(...args), ...args);
var value = typeof input === 'string' ? parseFloat(input) : typeof input === 'number' ? input : 0;
var pixels = Number.isNaN(value) ? 0 : value;
if (Math.abs(pixels) < 0) return pixels + 'px';
var mul = Math.pow(10, 5 + 1);
return (Math.round(Math.floor(((pixels 1) / 100) mul) / 10) * 10) / mul + 'rem';
}
`
source code:
`javascript
import styled from 'styled-components';
const height = '44';
export const ArrowFunction = styled.input.attrs(props => ({
type: 'password',
size: props.size || '16px',
width: props.width || 100,
}))
color: palevioletred;
font-size: 14px;
border: 1px solid palevioletred;
border-radius: 8px;
width: ${props => props.width}px; / MemberExpression Body /
height: ${() => height}px; / Identifier Body /
line-height: ${() => '44'}px; / StringLiteral Body /
margin: ${() => 32}px; / NumericLiteral Body /
padding: ${props => props.size};;
export const ArrowFunctionWithBlockBody = styled.button
width: ${props => {
if (props.width) {
return props.width;
} else {
return 0;
}
}}px; / Block Body /
${props => (props.disabled ? 'height: 400px' : 'height: 200px')};;
export const ArrowFunctionWithBinaryBody = styled.button
${props =>
props.disabled &&
};
width: 200px;
font-size: 14px;
height: ${props => !props.disabled && props.height}px; / LogicalExpression Body /
width: ${() => 44 + 50}px; / BinaryExpression Body /;
export const ArrowFunctionWithConditionalBody = styled.button
height: ${props => (props.height ? height : 100)}px; / ConditionalExpression Body /;`
compiled:
`javascript
import styled from 'styled-components';
const height = '44';
export const ArrowFunction = styled.input.attrs(props => ({
type: 'password',
size: props.size || '0.16rem',
width: props.width || 100,
}))
color: palevioletred;
font-size: 0.14rem;
border: 1px solid palevioletred;
border-radius: 0.08rem;
width: ${props => _pxtorem(props.width)}; / PropertyAccess Body /
height: ${() => _pxtorem(height)}; / Identifier Body /
line-height: ${() => _pxtorem('44')}; / StringLiteral Body /
margin: ${() => _pxtorem(32)}; / NumericLiteral Body /
padding: ${props => props.size};;
export const ArrowFunctionWithBlockBody = styled.button
width: ${props =>
_pxtorem(() => {
if (props.width) {
return props.width;
} else {
return 0;
}
})}; / Block Body /
${props => (props.disabled ? 'height: 4rem' : 'height: 2rem')};;
export const ArrowFunctionWithBinaryBody = styled.button
${props =>
props.disabled &&
};
width: 2rem;
font-size: 0.14rem;
height: ${props => _pxtorem(!props.disabled && props.height)}; / ArrowFunction with a LogicalExpression Body /
width: ${() => _pxtorem(44 + 50)}; / ArrowFunction with a BinaryExpression Body /;
export const ArrowFunctionWithConditionalBody = styled.button
height: ${props =>
props.height ? _pxtorem(height) : _pxtorem(100)}; / ArrowFunction with a ConditionalExpression Body /;
function _pxtorem(input, ...args) {
if (typeof input === 'function') return _pxtorem(input(...args), ...args);
var value = typeof input === 'string' ? parseFloat(input) : typeof input === 'number' ? input : 0;
var pixels = Number.isNaN(value) ? 0 : value;
if (Math.abs(pixels) < 0) return pixels + 'px';
var mul = Math.pow(10, 5 + 1);
return (Math.round(Math.floor(((pixels 1) / 100) mul) / 10) * 10) / mul + 'rem';
}
`
source code:
`javascript
import styled from 'styled-components';
export const MemberExpression = styled.button(
props =>
display: inline;
width: ${props.width}px;
height: ${props.height}; / Note: Only expression end with 'px' will be processed. /
font-size: 16px;,`
);
compiled:
`javascript
import styled from 'styled-components';
export const MemberExpression = styled.button(
props =>
display: inline;
width: ${_pxtorem(props.width)};
height: ${props.height}; / Note: Only expression end with 'px' will be processed. /
font-size: 0.16rem;,
);
function _pxtorem(input, ...args) {
if (typeof input === 'function') return _pxtorem(input(...args), ...args);
var value = typeof input === 'string' ? parseFloat(input) : typeof input === 'number' ? input : 0;
var pixels = Number.isNaN(value) ? 0 : value;
if (Math.abs(pixels) < 0) return pixels + 'px';
var mul = Math.pow(10, 5 + 1);
return (Math.round(Math.floor(((pixels 1) / 100) mul) / 10) * 10) / mul + 'rem';
}
`
source code:
`jsx harmony
import React from 'react';
import styled from 'styled-components';
export const ConditionalExpression = function({ fontSize } = {}) {
const StyledButton = styled.button
font-size: ${typeof fontSize === 'number' ? fontSize : props => props.theme.fontSize}px;
;
return
};
export const ConditionalExpressionWhenTrue = function({ fontSize } = {}) {
const StyledButton = styled.button
font-size: ${typeof fontSize !== 'number' ? props => props.theme.fontSize : fontSize}px;
;
return
};
export const ConditionalExpressionWhenFalse = function({ fontSize } = {}) {
const StyledButton = styled.button
font-size: ${typeof fontSize === 'number' ? fontSize : 16}px;
;
return
};
`
compiled:
`javascript
import React from 'react';
import styled from 'styled-components';
export const ConditionalExpression = function({ fontSize } = {}) {
const StyledButton = styled.button
font-size: ${typeof fontSize === 'number' ? _pxtorem(fontSize) : props => _pxtorem(props.theme.fontSize)};
;
return React.createElement(StyledButton, null);
};
export const ConditionalExpressionWhenTrue = function({ fontSize } = {}) {
const StyledButton = styled.button
font-size: ${typeof fontSize !== 'number' ? props => _pxtorem(props.theme.fontSize) : _pxtorem(fontSize)};
;
return React.createElement(StyledButton, null);
};
export const ConditionalExpressionWhenFalse = function({ fontSize } = {}) {
const StyledButton = styled.button
font-size: ${typeof fontSize === 'number' ? _pxtorem(fontSize) : _pxtorem(16)};
;
return React.createElement(StyledButton, null);
};
function _pxtorem(input, ...args) {
if (typeof input === 'function') return _pxtorem(input(...args), ...args);
var value = typeof input === 'string' ? parseFloat(input) : typeof input === 'number' ? input : 0;
var pixels = Number.isNaN(value) ? 0 : value;
if (Math.abs(pixels) < 0) return pixels + 'px';
var mul = Math.pow(10, 5 + 1);
return (Math.round(Math.floor(((pixels 1) / 100) mul) / 10) * 10) / mul + 'rem';
}
`
Identifier, CallExpression, BinaryExpress, StringLiteral, NumericLiteral, MemberExpression, LogicalExpression...
source code:
`javascript
import styled, { css, createGlobalStyle } from 'styled-components';
const fontSize = 18;
function getHeight() {
const height = 100;
return height / 2;
}
const mixins = css
padding: 0 16px;
margin: 16px 32px 16px 32px;;
export const GlobalStyle = createGlobalStyle
html body {
${mixins};
font-size: ${fontSize}px; / Identifier /
width: 1024px;
height: ${getHeight()}px; / CallExpression /
};
const condition = false;
function calc() {
return 20;
}
export const BinaryAndLogicExpression = styled.button
${condition ||
};
width: 200px;
height: ${condition || 100}px; / LogicExpression /
padding: ${40 + 50}px 8px ${4}px 16px; / BinaryExpression /
line-height: ${calc() - 2}px; / BinaryExpression /;`
compiled:
`javascript
import styled, { css, createGlobalStyle } from 'styled-components';
const fontSize = 18;
function getHeight() {
const height = 100;
return height / 2;
}
const mixins = css
padding: 0 0.16rem;
margin: 0.16rem 0.32rem 0.16rem 0.32rem;;
export const GlobalStyle = createGlobalStyle
html body {
${mixins};
font-size: ${_pxtorem(fontSize)}; / Identifier /
width: 10.24rem;
height: ${_pxtorem(getHeight())}; / CallExpression /
};
const condition = false;
function calc() {
return 20;
}
export const BinaryAndLogicExpression = styled.button
${condition ||
};
width: 2rem;
height: ${_pxtorem(condition || 100)};
padding: ${_pxtorem(40 + 50)} 0.08rem ${_pxtorem(4)} 0.16rem;
line-height: ${_pxtorem(calc() - 2)};;
function _pxtorem(input, ...args) {
if (typeof input === 'function') return _pxtorem(input(...args), ...args);
var value = typeof input === 'string' ? parseFloat(input) : typeof input === 'number' ? input : 0;
var pixels = Number.isNaN(value) ? 0 : value;
if (Math.abs(pixels) < 0) return pixels + 'px';
var mul = Math.pow(10, 5 + 1);
return (Math.round(Math.floor(((pixels 1) / 100) mul) / 10) * 10) / mul + 'rem';
}
``