Skip to content

Commit 95c23cf

Browse files
author
Brijesh Bittu
committed
[docs] Add how-it-works page
1 parent 909bbe9 commit 95c23cf

File tree

2 files changed

+139
-19
lines changed

2 files changed

+139
-19
lines changed
+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# How Pigment CSS works
2+
3+
Pigment CSS is a zero-runtime CSS-in-JS library. This means it does not have access to the end user's browser runtime which would be necessary to generate and insert authored CSS at runtime. Instead, it does all its processing at build time to pre-generate the CSS which then becomes part of the output bundle. This is why it cannot be consumed on its own. You must install the Pigment CSS plugin that corresponds to the framework you're using and configure your bundler accordingly (See the [integrations](/overview/integrations) page for more information).
4+
5+
## Processor
6+
7+
Pigment CSS uses the [WyW-in-JS](https://wyw-in-js.dev/) library that also powers [Linaria](https://linaria.dev/). It features a [processor](https://wyw-in-js.dev/how-to/custom-tagged-template#creating-a-processor) which makes it possible to create custom logic that's triggered by the presence of different imports from the library. The processor looks through the source code for `styled()`, `css()`, and other function calls and extracts the arguments to be evaluated. These values are then handed back to Pigment CSS for additional parsing and evaluation.
8+
9+
For example, here's a simple usage of the `css()` function:
10+
11+
```js
12+
// app.js
13+
import { css } from '@pigment-css/react-new';
14+
15+
const testClass = css(({ theme }) => ({
16+
padding: 0,
17+
position: 'relative',
18+
...theme.typography.body1,
19+
}));
20+
```
21+
22+
This snippet uses the `css()` call to generate and assign a class name string to the `testClass` variable.
23+
24+
The corresponding (minimal) bundler configuration for this to work with the theme might look like:
25+
26+
```ts title="bundler.config.ts"
27+
const customTheme = {
28+
typography: {
29+
body1: {
30+
fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
31+
fontWeight: 400,
32+
fontSize: 16,
33+
lineHeight: 1.5,
34+
// ...others
35+
},
36+
},
37+
};
38+
39+
const pigmentConfig = {
40+
theme: customTheme,
41+
};
42+
```
43+
44+
### 1. Detection and evaluation
45+
46+
When the source file above (`app.js`) goes through the bundler transform, Pigment CSS's bundler plugin looks for the call site of `css` imported from `@pigment-css/react` and prepares an intermediate source file to get the actual values of the arguments of the `css()` call. This happens through WyW-in-JS's processors. In this case, the simplified intermediate code is:
47+
48+
```js
49+
const _exp1 = ({ theme }) => ({
50+
padding: 0,
51+
position: 'relative',
52+
...theme.typography.body1,
53+
});
54+
55+
module.exports = {
56+
_exp1,
57+
};
58+
```
59+
60+
The above code is then evaluated through the use of built-in `node:vm` [module](https://nodejs.org/docs/latest/api/vm.html#class-vmscript) which returns the evaluated values of the parameters.
61+
62+
### 2. Transformation and runtime replacement
63+
64+
Once it has access to the actual values of the styles, Pigment CSS then does source code [transformation](https://github.com/mui/pigment-css/blob/master/packages/pigment-css-react-new/src/processors/css.ts) in place to remove the `css()` call from the source and replace it with a static class name string:
65+
66+
```js
67+
// app.js
68+
import { css } from '@pigment-css/react-new';
69+
70+
const testClass = 'c1aiqtje';
71+
```
72+
73+
### 3. Extraction
74+
75+
At the same time as the transformation in step 2, Pigment CSS also generates the CSS string for the above styles at build time. This generation happens through the `@emotion/serialize` package. In the [detection and evaluation](#1-detection-and-evaluation) step, Pigment gets access to the callback function defined as the first argument of the `css()` call. Since it's a function, it's called by Pigment CSS which also passes an object containing the `theme` as the first argument. This `theme` is the same object that was defined as part of the bundler config and passed to Pigment CSS. The returned object then goes through `@emotion/serialize` to generate the CSS string.
76+
77+
The output CSS string is then made part of the user code through the bundler integration:
78+
79+
```diff
80+
// app.js
81+
import { css } from '@pigment-css/react';
82+
+import './app.pigment.css';
83+
84+
const testClass = 'c1aiqtje';
85+
```
86+
87+
In the snippet above, you can assume that the `./app.pigment.css` import is a virtual module (this file doesn't actually exist on the file system) with the contents generated in-memory. This implementation varies across various bundlers but the general idea is the same. Here, the contents of the `app.pigment.css` file would be:
88+
89+
```css
90+
.c1aiqtje {
91+
padding: 0;
92+
position: relative;
93+
font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif;
94+
font-weight: 400;
95+
font-size: 16;
96+
line-height: 1.5;
97+
}
98+
```
99+
100+
## Debugging
101+
102+
Since Pigment CSS does all its processing at build time, debugging for errors works a little differently than it does with runtime CSS-in-JS. For example, when running the Emotion-based code snippet below (either in the browser or on the server) you'd be able to see the actual values of the `theme` object being logged at runtime in the browser console or server logs.
103+
104+
```js
105+
// app.js
106+
import { css } from '@emotion/styled';
107+
108+
const Paragraph = css(({ theme }) => {
109+
console.log(theme);
110+
return {
111+
padding: 0,
112+
position: 'relative',
113+
...theme.typography.body1,
114+
};
115+
});
116+
```
117+
118+
But if this same code were implemented with `@pigment-css/react-new` instead of `@emotion/styled`, you would see the theme being logged in your terminal at build time or during development (when you run `next dev`/`next build` or `vite dev`/`vite build` as appropriate). The actual output bundle won't have the code that logs the theme since it's already been replaced, as outlined in the steps above. In effect, the theme doesn't exist after bundling—it's reduced down to a collection of CSS variables.
119+
120+
Because of this, when debugging, you'll have to look in your terminal to locate issues specific to Pigment CSS. They'll start with `@pigment-css/` or `wyw-in-js` and include a short summary of why the issue is occurring. In many cases, issues arise related to the scope of the variables used to define the styles, so this is a good place to start looking if something isn't working as expected. Feel free to open a [new issue](https://github.com/mui/pigment-css/issues/new) when you encounter a problem that appears to be related to the Pigment CSS codebase itself.

docs/src/nav.ts

+19-19
Original file line numberDiff line numberDiff line change
@@ -59,25 +59,25 @@ export const nav: NavItem[] = [
5959
label: 'Composition',
6060
href: '/guides/composition',
6161
},
62-
{
63-
label: 'Migration',
64-
href: '/guides/migration',
65-
draft: true,
66-
},
67-
{
68-
label: 'UI Libraries',
69-
href: '/guides/ui-libraries',
70-
draft: true,
71-
},
72-
{
73-
label: 'Light and Dark modes',
74-
href: '/guides/light-and-dark-modes',
75-
},
76-
{
77-
label: 'RTL support',
78-
href: '/guides/rtl-support',
79-
draft: true,
80-
},
62+
// {
63+
// label: 'Migration',
64+
// href: '/guides/migration',
65+
// draft: true,
66+
// },
67+
// {
68+
// label: 'UI Libraries',
69+
// href: '/guides/ui-libraries',
70+
// draft: true,
71+
// },
72+
// {
73+
// label: 'Light and Dark modes',
74+
// href: '/guides/light-and-dark-modes',
75+
// },
76+
// {
77+
// label: 'RTL support',
78+
// href: '/guides/rtl-support',
79+
// draft: true,
80+
// },
8181
],
8282
},
8383
{

0 commit comments

Comments
 (0)