diff --git a/.github/ISSUE_TEMPLATE/css-issue.yml b/.github/ISSUE_TEMPLATE/css-issue.yml
index e42dd65f3..02dbb2a02 100644
--- a/.github/ISSUE_TEMPLATE/css-issue.yml
+++ b/.github/ISSUE_TEMPLATE/css-issue.yml
@@ -125,6 +125,7 @@ body:
- PostCSS Replace Overflow Wrap
- PostCSS Scope Pseudo Class
- PostCSS Selector Not
+ - PostCSS Sign Functions
- PostCSS Slow Plugins
- PostCSS Stepped Value Functions
- PostCSS System Ui Font Family
diff --git a/.github/ISSUE_TEMPLATE/plugin-issue.yml b/.github/ISSUE_TEMPLATE/plugin-issue.yml
index 554cdfcfe..91bab5022 100644
--- a/.github/ISSUE_TEMPLATE/plugin-issue.yml
+++ b/.github/ISSUE_TEMPLATE/plugin-issue.yml
@@ -122,6 +122,7 @@ body:
- PostCSS Replace Overflow Wrap
- PostCSS Scope Pseudo Class
- PostCSS Selector Not
+ - PostCSS Sign Functions
- PostCSS Slow Plugins
- PostCSS Stepped Value Functions
- PostCSS System Ui Font Family
diff --git a/.github/labeler.yml b/.github/labeler.yml
index 263ec4322..c918d0a26 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -425,6 +425,12 @@
- plugins/postcss-selector-not/**
- experimental/postcss-selector-not/**
+"plugins/postcss-sign-functions":
+ - changed-files:
+ - any-glob-to-any-file:
+ - plugins/postcss-sign-functions/**
+ - experimental/postcss-sign-functions/**
+
"plugins/postcss-slow-plugins":
- changed-files:
- any-glob-to-any-file:
diff --git a/package-lock.json b/package-lock.json
index 4c29b5278..1dedbfc97 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1996,6 +1996,10 @@
"resolved": "plugins/postcss-scope-pseudo-class",
"link": true
},
+ "node_modules/@csstools/postcss-sign-functions": {
+ "resolved": "plugins/postcss-sign-functions",
+ "link": true
+ },
"node_modules/@csstools/postcss-slow-plugins": {
"resolved": "plugins/postcss-slow-plugins",
"link": true
@@ -10909,6 +10913,34 @@
"postcss": "^8.4"
}
},
+ "plugins/postcss-sign-functions": {
+ "version": "0.0.0",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "dependencies": {
+ "@csstools/css-calc": "^2.0.4",
+ "@csstools/css-parser-algorithms": "^3.0.4",
+ "@csstools/css-tokenizer": "^3.0.3"
+ },
+ "devDependencies": {
+ "@csstools/postcss-tape": "*"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
"plugins/postcss-slow-plugins": {
"name": "@csstools/postcss-slow-plugins",
"version": "2.0.0",
diff --git a/plugins/postcss-sign-functions/.gitignore b/plugins/postcss-sign-functions/.gitignore
new file mode 100644
index 000000000..e5b28db4a
--- /dev/null
+++ b/plugins/postcss-sign-functions/.gitignore
@@ -0,0 +1,6 @@
+node_modules
+package-lock.json
+yarn.lock
+*.result.css
+*.result.css.map
+*.result.html
diff --git a/plugins/postcss-sign-functions/.nvmrc b/plugins/postcss-sign-functions/.nvmrc
new file mode 100644
index 000000000..6ed5da955
--- /dev/null
+++ b/plugins/postcss-sign-functions/.nvmrc
@@ -0,0 +1 @@
+v20.2.0
diff --git a/plugins/postcss-sign-functions/CHANGELOG.md b/plugins/postcss-sign-functions/CHANGELOG.md
new file mode 100644
index 000000000..838c15423
--- /dev/null
+++ b/plugins/postcss-sign-functions/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Changes to PostCSS Sign Functions
+
+### Unreleased (major)
+
+- Initial version
diff --git a/plugins/postcss-sign-functions/INSTALL.md b/plugins/postcss-sign-functions/INSTALL.md
new file mode 100644
index 000000000..e190aba65
--- /dev/null
+++ b/plugins/postcss-sign-functions/INSTALL.md
@@ -0,0 +1,235 @@
+# Installing PostCSS Sign Functions
+
+[PostCSS Sign Functions] runs in all Node environments, with special instructions for:
+
+- [Node](#node)
+- [PostCSS CLI](#postcss-cli)
+- [PostCSS Load Config](#postcss-load-config)
+- [Webpack](#webpack)
+- [Next.js](#nextjs)
+- [Gulp](#gulp)
+- [Grunt](#grunt)
+
+
+
+## Node
+
+Add [PostCSS Sign Functions] to your project:
+
+```bash
+npm install postcss @csstools/postcss-sign-functions --save-dev
+```
+
+Use it as a [PostCSS] plugin:
+
+```js
+// commonjs
+const postcss = require('postcss');
+const postcssSignFunctions = require('@csstools/postcss-sign-functions');
+
+postcss([
+ postcssSignFunctions(/* pluginOptions */)
+]).process(YOUR_CSS /*, processOptions */);
+```
+
+```js
+// esm
+import postcss from 'postcss';
+import postcssSignFunctions from '@csstools/postcss-sign-functions';
+
+postcss([
+ postcssSignFunctions(/* pluginOptions */)
+]).process(YOUR_CSS /*, processOptions */);
+```
+
+## PostCSS CLI
+
+Add [PostCSS CLI] to your project:
+
+```bash
+npm install postcss-cli @csstools/postcss-sign-functions --save-dev
+```
+
+Use [PostCSS Sign Functions] in your `postcss.config.js` configuration file:
+
+```js
+const postcssSignFunctions = require('@csstools/postcss-sign-functions');
+
+module.exports = {
+ plugins: [
+ postcssSignFunctions(/* pluginOptions */)
+ ]
+}
+```
+
+## PostCSS Load Config
+
+If your framework/CLI supports [`postcss-load-config`](https://github.com/postcss/postcss-load-config).
+
+```bash
+npm install @csstools/postcss-sign-functions --save-dev
+```
+
+`package.json`:
+
+```json
+{
+ "postcss": {
+ "plugins": {
+ "@csstools/postcss-sign-functions": {}
+ }
+ }
+}
+```
+
+`.postcssrc.json`:
+
+```json
+{
+ "plugins": {
+ "@csstools/postcss-sign-functions": {}
+ }
+}
+```
+
+_See the [README of `postcss-load-config`](https://github.com/postcss/postcss-load-config#usage) for more usage options._
+
+## Webpack
+
+_Webpack version 5_
+
+Add [PostCSS Loader] to your project:
+
+```bash
+npm install postcss-loader @csstools/postcss-sign-functions --save-dev
+```
+
+Use [PostCSS Sign Functions] in your Webpack configuration:
+
+```js
+module.exports = {
+ module: {
+ rules: [
+ {
+ test: /\.css$/i,
+ use: [
+ "style-loader",
+ {
+ loader: "css-loader",
+ options: { importLoaders: 1 },
+ },
+ {
+ loader: "postcss-loader",
+ options: {
+ postcssOptions: {
+ plugins: [
+ // Other plugins,
+ [
+ "@csstools/postcss-sign-functions",
+ {
+ // Options
+ },
+ ],
+ ],
+ },
+ },
+ },
+ ],
+ },
+ ],
+ },
+};
+```
+
+## Next.js
+
+Read the instructions on how to [customize the PostCSS configuration in Next.js](https://nextjs.org/docs/advanced-features/customizing-postcss-config)
+
+```bash
+npm install @csstools/postcss-sign-functions --save-dev
+```
+
+Use [PostCSS Sign Functions] in your `postcss.config.json` file:
+
+```json
+{
+ "plugins": [
+ "@csstools/postcss-sign-functions"
+ ]
+}
+```
+
+```json5
+{
+ "plugins": [
+ [
+ "@csstools/postcss-sign-functions",
+ {
+ // Optionally add plugin options
+ }
+ ]
+ ]
+}
+```
+
+## Gulp
+
+Add [Gulp PostCSS] to your project:
+
+```bash
+npm install gulp-postcss @csstools/postcss-sign-functions --save-dev
+```
+
+Use [PostCSS Sign Functions] in your Gulpfile:
+
+```js
+const postcss = require('gulp-postcss');
+const postcssSignFunctions = require('@csstools/postcss-sign-functions');
+
+gulp.task('css', function () {
+ var plugins = [
+ postcssSignFunctions(/* pluginOptions */)
+ ];
+
+ return gulp.src('./src/*.css')
+ .pipe(postcss(plugins))
+ .pipe(gulp.dest('.'));
+});
+```
+
+## Grunt
+
+Add [Grunt PostCSS] to your project:
+
+```bash
+npm install grunt-postcss @csstools/postcss-sign-functions --save-dev
+```
+
+Use [PostCSS Sign Functions] in your Gruntfile:
+
+```js
+const postcssSignFunctions = require('@csstools/postcss-sign-functions');
+
+grunt.loadNpmTasks('grunt-postcss');
+
+grunt.initConfig({
+ postcss: {
+ options: {
+ processors: [
+ postcssSignFunctions(/* pluginOptions */)
+ ]
+ },
+ dist: {
+ src: '*.css'
+ }
+ }
+});
+```
+
+[Gulp PostCSS]: https://github.com/postcss/gulp-postcss
+[Grunt PostCSS]: https://github.com/nDmitry/grunt-postcss
+[PostCSS]: https://github.com/postcss/postcss
+[PostCSS CLI]: https://github.com/postcss/postcss-cli
+[PostCSS Loader]: https://github.com/postcss/postcss-loader
+[PostCSS Sign Functions]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-sign-functions
+[Next.js]: https://nextjs.org
diff --git a/plugins/postcss-sign-functions/LICENSE.md b/plugins/postcss-sign-functions/LICENSE.md
new file mode 100644
index 000000000..e8ae93b9f
--- /dev/null
+++ b/plugins/postcss-sign-functions/LICENSE.md
@@ -0,0 +1,18 @@
+MIT No Attribution (MIT-0)
+
+Copyright © CSSTools Contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the “Software”), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/plugins/postcss-sign-functions/README.md b/plugins/postcss-sign-functions/README.md
new file mode 100644
index 000000000..3d955e6fb
--- /dev/null
+++ b/plugins/postcss-sign-functions/README.md
@@ -0,0 +1,180 @@
+# PostCSS Sign Functions [][PostCSS]
+
+[
][npm-url] [
][cli-url] [
][discord]
[][css-url] [
][css-url]
+
+```bash
+npm install @csstools/postcss-sign-functions --save-dev
+```
+
+[PostCSS Sign Functions] lets you use the `sign` and `abs` functions, following the [CSS Values 4].
+
+```pcss
+.sign {
+ z-index: sign(-10px);
+}
+
+.sign {
+ z-index: sign(0);
+}
+
+.sign {
+ z-index: sign(10px);
+}
+
+.abs {
+ z-index: abs(-10px);
+}
+
+.abs {
+ z-index: abs(0);
+}
+
+.abs {
+ z-index: abs(10px);
+}
+
+/* becomes */
+
+.sign {
+ z-index: -1;
+ z-index: sign(-10px);
+}
+
+.sign {
+ z-index: 0;
+ z-index: sign(0);
+}
+
+.sign {
+ z-index: 1;
+ z-index: sign(10px);
+}
+
+.abs {
+ z-index: 10px;
+ z-index: abs(-10px);
+}
+
+.abs {
+ z-index: 0;
+ z-index: abs(0);
+}
+
+.abs {
+ z-index: 10px;
+ z-index: abs(10px);
+}
+```
+
+> [!NOTE]
+> The utility of static fallbacks for `sign` and `abs` is limited.
+> The most interesting values are variables and dynamic values (e.g. those containing `%`).
+> It is impossible to generate static fallbacks in a build process for values that are dynamic on the client.
+
+## Usage
+
+Add [PostCSS Sign Functions] to your project:
+
+```bash
+npm install postcss @csstools/postcss-sign-functions --save-dev
+```
+
+Use it as a [PostCSS] plugin:
+
+```js
+const postcss = require('postcss');
+const postcssSignFunctions = require('@csstools/postcss-sign-functions');
+
+postcss([
+ postcssSignFunctions(/* pluginOptions */)
+]).process(YOUR_CSS /*, processOptions */);
+```
+
+[PostCSS Sign Functions] runs in all Node environments, with special
+instructions for:
+
+- [Node](INSTALL.md#node)
+- [PostCSS CLI](INSTALL.md#postcss-cli)
+- [PostCSS Load Config](INSTALL.md#postcss-load-config)
+- [Webpack](INSTALL.md#webpack)
+- [Next.js](INSTALL.md#nextjs)
+- [Gulp](INSTALL.md#gulp)
+- [Grunt](INSTALL.md#grunt)
+
+## ⚠️ About custom properties
+
+Given the dynamic nature of custom properties it's impossible to know what the variable value is, which means the plugin can't compute a final value for the stylesheet.
+
+Because of that, any usage that contains a `var` is skipped.
+
+## Options
+
+### preserve
+
+The `preserve` option determines whether the original notation
+is preserved. By default, it is preserved.
+
+```js
+postcssSignFunctions({ preserve: false })
+```
+
+```pcss
+.sign {
+ z-index: sign(-10px);
+}
+
+.sign {
+ z-index: sign(0);
+}
+
+.sign {
+ z-index: sign(10px);
+}
+
+.abs {
+ z-index: abs(-10px);
+}
+
+.abs {
+ z-index: abs(0);
+}
+
+.abs {
+ z-index: abs(10px);
+}
+
+/* becomes */
+
+.sign {
+ z-index: -1;
+}
+
+.sign {
+ z-index: 0;
+}
+
+.sign {
+ z-index: 1;
+}
+
+.abs {
+ z-index: 10px;
+}
+
+.abs {
+ z-index: 0;
+}
+
+.abs {
+ z-index: 10px;
+}
+```
+
+[cli-url]: https://github.com/csstools/postcss-plugins/actions/workflows/test.yml?query=workflow/test
+[css-url]: https://cssdb.org/#sign-functions
+[discord]: https://discord.gg/bUadyRwkJS
+[npm-url]: https://www.npmjs.com/package/@csstools/postcss-sign-functions
+
+[PostCSS]: https://github.com/postcss/postcss
+[PostCSS Sign Functions]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-sign-functions
+[CSS Values 4]: https://drafts.csswg.org/css-values-4/#sign-funcs
diff --git a/plugins/postcss-sign-functions/api-extractor.json b/plugins/postcss-sign-functions/api-extractor.json
new file mode 100644
index 000000000..42058be51
--- /dev/null
+++ b/plugins/postcss-sign-functions/api-extractor.json
@@ -0,0 +1,4 @@
+{
+ "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
+ "extends": "../../api-extractor.json"
+}
diff --git a/plugins/postcss-sign-functions/dist/index.cjs b/plugins/postcss-sign-functions/dist/index.cjs
new file mode 100644
index 000000000..ab0feb4f7
--- /dev/null
+++ b/plugins/postcss-sign-functions/dist/index.cjs
@@ -0,0 +1 @@
+"use strict";var s=require("@csstools/css-calc");const e=/(?{const t=Object.assign({preserve:!0},c);return{postcssPlugin:"postcss-sign-functions",Declaration(c){if(!e.test(c.value))return;const o=s.calc(c.value,{precision:5,toCanonicalUnits:!0});o!==c.value&&(c.cloneBefore({value:o}),t.preserve||c.remove())}}};creator.postcss=!0,module.exports=creator;
diff --git a/plugins/postcss-sign-functions/dist/index.d.ts b/plugins/postcss-sign-functions/dist/index.d.ts
new file mode 100644
index 000000000..79981d076
--- /dev/null
+++ b/plugins/postcss-sign-functions/dist/index.d.ts
@@ -0,0 +1,12 @@
+import type { PluginCreator } from 'postcss';
+
+declare const creator: PluginCreator