tpa-style-webpack-plugin
Version:
A Webpack plugin that handles wix tpa styles, it separates static css file that injects dynamic style at runtime.
188 lines (145 loc) • 8.57 kB
Markdown
# tpa-style-webpack-plugin
[![Build Status][ci-img]][ci] [![NPM version][npm-img]][npm] [![code style: prettier][prettier-img]][prettier]
[prettier-img]: https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat
[prettier]: https://github.com/prettier/prettier
[ci-img]: https://travis-ci.org/wix-incubator/tpa-style-webpack-plugin.svg?branch=master
[ci]: https://travis-ci.org/wix-incubator/tpa-style-webpack-plugin
[npm-img]: https://img.shields.io/npm/v/tpa-style-webpack-plugin.svg
[npm]: https://www.npmjs.com/package/tpa-style-webpack-plugin
A Webpack plugin that handles wix tpa styles, it extracts the dynamic css from the static css and injects it back to the bundle.
## Requirements
- Node.js v8 or above
- Webpack 4.x
## Installation
```sh
$ npm install --save-dev tpa-style-webpack-plugin
```
## Usage
Add the plugin to your webpack config.
<!-- prettier-ignore-start -->
```js
// webpack.config.js
const TpaStyleWebpackPlugin = require('tpa-style-webpack-plugin');
module.exports = {
module: {
...
},
plugins: [
new MiniCssExtractPlugin({filename: '[name].[chunkhash].css'}),
new TpaStyleWebpackPlugin()
]
};
```
## getProcessedCss
import [`getProcessedCss`](https://github.com/wix-incubator/tpa-style-webpack-plugin/blob/master/src/runtime/main.ts#L21) function from plugins runtime in your production code.
```js
import {getProcessedCss} from 'tpa-style-webpack-plugin/runtime';
import {addStyles} from 'tpa-style-webpack-plugin/addStyles';
const dynamicCss = getProcessedCss(
{styleParams, siteColors, siteTextPresets},
{isRTL: false, prefixSelector: '.style-id', strictMode: true}
);
addStyles(dynamicCss, 'tag-id');
```
<!-- prettier-ignore-end -->
#### `getProcessedCss` Options
| Name | Type | Default | Description |
| :----------------: | :---------: | :-----: | :---------------------------------------------------------------------------------- |
| **isRTL** | `{Boolean}` | `false` | Defines id the [direction replacers](#direction-support) will work on ltr/rtl mode. |
| **prefixSelector** | `{String}` | `''` | Prefix of each selector in the css |
| **strictMode** | `{Boolean}` | `true` | Defines if the function should throw on invalid css or invalid values. |
### Supported Css Functions
```css
.my-selector {
--my-font: "font(Body-M)"; /* define a custom variable with a default value */
--my-font2: "font({theme: 'Body-M', size: '10px', lineHeight: '2em', weight: 'bold', style:'italic'})" /* will use Body-M as base font and override the given attributes */
--default-width: "number(42)"; /* define a numeric custom var */
font: "font({theme: 'var-from-settings', size: '32px', lineHeight: '40px'})"; /* will use var-from-settings as base font and override the given attributes */
font: "font(--my-font)"; /* assign a dynamic font value from a custom var */
width: calc(100% - '"unit(--default-width, px)"'); /* assign a dynamic numeric value from a custom var */
width: calc(100% / "number(--default-width)" + 0px); /* assign a dynamic numeric value from a custom var */
color: "color(color-8)"; /* assign a color from the site's palette */
background-color: "join(opacity(color-1, 0.5), opacity(color-8, 0.5))"; /* blends 2 colors */
color: "opacity(color-8, 0.3)"; /* add opacity to a site palette color */
color: "withoutOpacity(opacity(color-8, 0.3))"; /* will remove the opacity of site palette color */
color: "darken(color-8, 0.3)"; /* make a darken version of site palette color */
color: "whiten(color-8, 0.3)"; /* make a whiten version of site palette color - Mix the color with pure white, from 0 to 100 */
color: "lighten(color-8, 0.3)"; /* make a lighten version of site palette color - from 0 to 100. Providing 100 will always return white */
font: "font(--my-font2)"; /* will use the overridden default unless it was defined in settings */
border-width: "unit(--var-from-settings, px)"; /* will produce border-width: 42px */
color: "fallback(color(--var-from-settings), color(color-8))"; /* will return the first none falsy value from left to right */
}
```
### Direction support
| Expression | RTL | LTR |
| ---------- | ----- | ----- |
| START | right | left |
| END | left | right |
| STARTSIGN | '' | '-' |
| ENDSIGN | '-' | '' |
| DEG-START | 180 | 0 |
| DEG-END | 0 | 180 |
| DIR | rtl | ltr |
```css
.my-selector {
padding-start: 9px; /* START will be replaced with left / right, given rtl = false / true */
float: START; /* Same as above, applied to the value */
padding-end: 9px; /* END will be replaced with left / right, given rtl = true / false */
direction: DIR; /* DIR will be replaced with ltr / rtl, given rtl = false / true */
margin: STARTSIGN5px; /* STARTSIGN will be replaced with -, given rtl = false, and will be removed for rtl = true */
margin: ENDSIGN5px; /* ENDSIGN will be replaced with -, given rtl = true, and will be removed for rtl = false */
transform: rotate(DEG-STARTdeg);
transform: rotate(DEG-ENDdeg);
}
```
You can check out an [example project](https://github.com/felixmosh/extract-tpa-style-test).
## getStaticCss
Use it to inject the static css content to your .js bundle
## Comparison to [wix-style-processor](https://github.com/wix/wix-style-processor)
This plugin was written in order to add support for **SSR** to Wix tpa dynamic styles as part of OOI project.
It uses the same syntax as `wix-style-proccesor` but it works completely different, instead of searching the DOM for style tags with special syntax, it extracts the special syntax css, prepare all the data-structure that is needed at **build-time** and then exposes a function that given site params (colors, settings etc...) at **run-time** returns the `css` as a **string**.
You can use a [built in method](https://github.com/wix-incubator/tpa-style-webpack-plugin/blob/master/addStyles.js#L34) to inject it as a style tag or use your framework to render the style tag.
### Usage example (React)
```javascript
import React from "react";
import {getProcessedCss} from "tpa-style-webpack-plugin/runtime";
export const withStyles = (Component, options) => {
return function WithStyles(props) {
const {isRTL, siteStyles, styleId} = props;
const dynamicCss = getProcessedCss(siteStyles, {
prefixSelector: styleId ? `.${styleId}` : "",
isRTL: !!isRTL,
strictMode: !!options.strictMode,
});
return (
<div className={styleId}>
<style dangerouslySetInnerHTML={{__html: dynamicCss}} />
<Component {...props} />
</div>
);
};
};
```
## ⚠️ Caveats
- If you use [cssnano](https://cssnano.co/) to minify CSS, be aware of the following limitations:
- Don't mix static and dynamic values inside of `border` shorthand declaration ([see the issue](https://github.com/cssnano/cssnano/issues/402)):
```css
.my-selector {
/* DON'T */
border: "unit(--var-from-settings, px)" solid "color(color-1)"; /* `cssnano` will remove the dynamic values, and it will become "border: solid;" */
/* DO */
border-width: "unit(--var-from-settings, px)";
border-style: solid;
border-color: "color(color-1)";
}
```
- If you use percentages and dynamic values together inside of `calc`, you need to have mixed units in order to [opt out of `calc` minification](https://github.com/MoOx/reduce-css-calc/pull/9/files#diff-168726dbe96b3ce427e7fedce31bb0bcR54):
```css
.my-selector {
/* DON'T */
width: calc(100% / "number(4)"); /* `reduce-css-calc` (used by `cssnano`) will transform it to "calc(1 / 4)" */
/* DO */
width: calc(100% / "number(4)" + 0px); /* notice 0px here */
}
```
- Currently there is no support for usage of [`style-loader`](https://github.com/webpack-contrib/style-loader), coming soon.