PostCSS plugin that allows users to define custom functions using JavaScript. This plugin is inspired by custom functions in CSS.
For example, define a custom function:
module.exports = {
plugins: {
'@yuheiy/postcss-custom-functions': {
functions: {
'--negative': (value) => `calc(-1 * ${value})`,
},
},
},
};
Use the custom function you have defined:
html {
--gap: 1em;
padding: --negative(var(--gap));
/* or by passing the value explicitly, like: */
padding: --negative(1em);
}
will be processed to:
html {
--gap: 1em;
padding: calc(-1 * var(--gap));
/* or by passing the value explicitly, like: */
padding: calc(-1 * 1em);
}
Step 1: Install plugin:
npm install --save-dev postcss @yuheiy/postcss-custom-functions
Step 2: Check you project for existed PostCSS config: postcss.config.js
in the project root, "postcss"
section in package.json
or postcss
in bundle config.
If you do not use PostCSS, add it according to official docs and set this plugin in settings.
Step 3: Add the plugin to plugins list:
module.exports = {
plugins: {
+ '@yuheiy/postcss-custom-functions': {},
'autoprefixer': {},
},
};
Type: { [key: string]: (...values: string[]) => string }
Default: {}
Define custom functions that can be used in your CSS.
You can define custom functions with optional arguments and fallback values.
function shadow(shadowColor = 'var(--shadow-color, black)') {
return `2px 2px ${shadowColor}`;
}
module.exports = {
plugins: {
'@yuheiy/postcss-custom-functions': {
functions: {
'--shadow': shadow,
},
},
},
};
Use the custom function you have defined:
.foo {
--shadow-color: blue;
box-shadow: --shadow(); /* produces a blue shadow */
/* or just */
box-shadow: --shadow(blue);
}
will be processed to:
.foo {
--shadow-color: blue;
box-shadow: 2px 2px var(--shadow-color, black); /* produces a blue shadow */
/* or just */
box-shadow: 2px 2px blue;
}
You can validate functions arguments and output warnings if they are incorrect.
function negative(value, ...rest) {
if (!value) {
throw new Error(
`The --negative(…) function requires an argument, but received none.`,
);
}
if (rest.length > 0) {
throw new Error(
`The --negative(…) function only accepts a single argument, but received ${
rest.length + 1
}.`,
);
}
return `calc(-1 * ${value})`;
}
module.exports = {
plugins: {
'@yuheiy/postcss-custom-functions': {
functions: {
'--negative': negative,
},
},
},
};
This implementation is inspired by Tailwind CSS’s css-functions.js
.
You can also implement Fluid Typography as a custom function, using the tan(atan2())
technique to remove px units and calculate them in CSS.
function fluid(
minSize,
maxSize,
minBreakpoint = 'var(--breakpoint-sm)',
maxBreakpoint = 'var(--breakpoint-xl)',
...rest
) {
if (!minSize || !maxSize) {
throw new Error(
'The --fluid(…) function requires 2–4 arguments, but received none.',
);
}
if (rest.length > 0) {
throw new Error(
`The --fluid(…) function only accepts 4 arguments, but received ${
rest.length + 1
}.`,
);
}
const slope = `calc(tan(atan2(${maxSize} - ${minSize}, 1px)) / tan(atan2(${maxBreakpoint} - ${minBreakpoint}, 1px)))`;
const intercept = `calc(tan(atan2(${minSize}, 1px)) - ${slope} * tan(atan2(${minBreakpoint}, 1px)))`;
return `clamp(${[
`min(${minSize}, ${maxSize})`,
`${slope} * 100svw + ${intercept} / 16 * 1rem`,
`max(${minSize}, ${maxSize})`,
].join(', ')})`;
}
module.exports = {
plugins: {
'@yuheiy/postcss-custom-functions': {
functions: {
'--fluid': fluid,
},
},
},
};
Use the custom function you have defined:
:root {
--breakpoint-sm: 40rem;
--breakpoint-md: 48rem;
--breakpoint-lg: 64rem;
--breakpoint-xl: 80rem;
--breakpoint-2xl: 96rem;
}
h1 {
font-size: --fluid(2rem, 4rem);
}
will be processed to:
:root {
--breakpoint-sm: 40rem;
--breakpoint-md: 48rem;
--breakpoint-lg: 64rem;
--breakpoint-xl: 80rem;
--breakpoint-2xl: 96rem;
}
h1 {
font-size: clamp(min(2rem, 4rem), calc(tan(atan2(4rem - 2rem, 1px)) / tan(atan2(var(--breakpoint-xl) - var(--breakpoint-sm), 1px))) * 100svw + calc(tan(atan2(2rem, 1px)) - calc(tan(atan2(4rem - 2rem, 1px)) / tan(atan2(var(--breakpoint-xl) - var(--breakpoint-sm), 1px))) * tan(atan2(var(--breakpoint-sm), 1px))) / 16 * 1rem, max(2rem, 4rem));
}