Skip to content

Commit

Permalink
docs: add pages about Atomic CSS, build time optimisations
Browse files Browse the repository at this point in the history
  • Loading branch information
layershifter committed Sep 27, 2022
1 parent e5edea9 commit f49dbca
Show file tree
Hide file tree
Showing 21 changed files with 1,488 additions and 182 deletions.
117 changes: 0 additions & 117 deletions .yarn/patches/@docusaurus-theme-common-npm-2.0.0-beta.15-aaa76f45ef

This file was deleted.

13 changes: 13 additions & 0 deletions .yarn/patches/mdx-mermaid-npm-1.3.2-e8a6432fce.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
diff --git a/lib/Mermaid.mjs b/lib/Mermaid.mjs
index 5140cd5e556ede3debb33ae2202f04480b288d2f..204a3f97d590c9a384ed1bdcfcf7b0dc1375c616 100644
--- a/lib/Mermaid.mjs
+++ b/lib/Mermaid.mjs
@@ -45,7 +45,7 @@ const Mermaid = ({ chart, config: configSrc }) => {
if (typeof window === 'undefined') {
return React.createElement("div", { className: "mermaid", "data-mermaid-src": chart }, chart);
}
- const config = useMemo(() => typeof configSrc === 'string' ? JSON.parse(configSrc) : configSrc, [configSrc]);
+ const config = useMemo(() => typeof configSrc === 'string' ? JSON.parse(configSrc) : configSrc || {}, [configSrc]);
const html = document.querySelector('html');
const [rerender, setRerender] = useState(false);
const theme = useMemo(() => getTheme(html, config), [config, rerender]);
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"label": "Ahead-of-time compilation",
"collapsed": false,
"position": 4
}
14 changes: 14 additions & 0 deletions apps/website/docs/react/ahead-of-time-compilation/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
sidebar_position: 1
---

# Introduction

While there is nothing wrong with the associated runtime costs of a CSS-in-JS engine, larger and more complex applications might want to optimize for performance.

Griffel does the expensive runtime work only happens on the first render of the component. This one-time work can be further optimized at build time by pre-computing and transforming styles.

## What to use?

- For library developers, please use [Babel preset](/docs/react/ahead-of-time-compilation/with-babel)
- For application developers, please use [Webpack loader](/docs/react/ahead-of-time-compilation/with-webpack) (supports Next.js)
146 changes: 146 additions & 0 deletions apps/website/docs/react/ahead-of-time-compilation/technical-details.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
---
sidebar_position: 4
---

# Technical details

## What is being optimized with AOT?

:::info

Style resolution only needs to happen on the initial render of a component. Therefore, without build time optimization the performance is comparable with the 2nd and consecutive renders.

It is reasonable to introduce build time optimization if/when it is required.

:::

```jsx
import { makeStyles } from '@griffel/react';

// 1. Invocation of makeStyles creates a styling hook that will be used inside a component.
const useStyles = makeStyles({
root: { paddingLeft: '1px', display: 'flex' },
});

function Component() {
// 2. The hook call resolves styles which are injected into the document.
const classes = useStyles();

return <div className={classes.root} />;
}
```

You can look at the graph below which describes what work is done during style resolution:

```mermaid
stateDiagram-v2
direction LR
INVOKE_USE_STYLES: useStyles() invocation
COMPUTE_RTL_STYLES: Compute RTL styles
COMPUTE_CSS_CLASSES: Compute CSS classes
COMPUTE_CSS_RULES: Compute CSS rules
INSERT_TO_DOM: Insert to DOM
INVOKE_USE_STYLES --> COMPUTE_RTL_STYLES
COMPUTE_RTL_STYLES --> COMPUTE_CSS_CLASSES
COMPUTE_CSS_CLASSES --> COMPUTE_CSS_RULES
COMPUTE_CSS_RULES --> INSERT_TO_DOM
note right of INVOKE_USE_STYLES
{
&nbsp;&nbsp;paddingLeft: '1px',
&nbsp;&nbsp;display: 'flex'
}
end note
note right of COMPUTE_RTL_STYLES
{
&nbsp;&nbsp;paddingLeft: '1px',
&nbsp;&nbsp;paddingRight: '1px',
&nbsp;&nbsp;display: 'flex'
}
end note
note right of COMPUTE_CSS_CLASSES
{
&nbsp;&nbsp;paddingLeft: '1px', // .f10xn8zz
&nbsp;&nbsp;paddingRight: '1px', // .f136y8j8
&nbsp;&nbsp;display: 'flex' // .f22iagw
}
end note
note right of COMPUTE_CSS_RULES
.f10xn8zz { padding-left: 1px }
.f136y8j8 { padding-right: 1px }
.f22iagw { display: flex }
end note
```

:::note

This work only happens once, during first render.

:::

The final result before the CSS rules are inserted into DOM can be compiled ahead of time during build time through the methods described above.
Once the styles of our simple example are transformed at build time the resulting bundle contains a result similar to what is in our diagram.

The actual runtime code of `makeStyles` is completely stripped from the bundle and replaced with a lightweight function (`__styles`) that simply concatenates the CSS classes and inserts them to DOM.

```jsx
const useStyles = __styles(
{
root: {
mc9l5x: 'f22iagw',
uwmqm3: ['f10xn8zz', 'f136y8j8'],
},
},
{
d: ['.f22iagw{display:flex;}', '.f10xn8zz{padding-left:1px;}', '.f136y8j8{padding-right:1px;}'],
},
);

function Component() {
const classes = useStyles();

return <div className={classes.root} />;
}
```

## Module evaluation process

Let's consider the following scenario:

```js
// constants.js
export const PADDING_TOKEN = '1px';
```

```js
// common.js
export const commonStyles = () => ({
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
});
```

```js
// styles.js
import { makeStyles } from '@griffel/react';
import { PADDING_TOKEN } from './constants';
import { commonStyles } from './common';

const useStyles = makeStyles({
root: { paddingLeft: PADDING_TOKEN, ...commonStyles() },
});
```

It's perfectly fine, and even recommended to reuse common tokens and create style helpers across an application.
It's one of the main benefits of using CSS-in-JS.

However, this means that the build time transforms which are described above are not trivial to compute because code needs to be evaluated to know what styles to transform.
In the example above, in order to transform the `styles.js` file, the code needs to be executed/evaluated by importing the extra modules that it depends on (`constants.js` and `common.js`).

Griffel uses style evaluation from [Linaria](https://linaria.dev/).
The build-time evaluation happens as a part of the Babel transforms in Griffel. All styles that require evaluation will be batched and done in single evaluation context.
Linaria's Babel config is separate to any config used by the application.
Therefore, additional language features may require [extra configuration](/react/ahead-of-time-compilation/with-babel#configuration).
41 changes: 41 additions & 0 deletions apps/website/docs/react/ahead-of-time-compilation/with-babel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
sidebar_position: 3
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# With Babel

## Install

<Tabs>
<TabItem value="yarn" label="Yarn">

```shell
yarn add --dev @griffel/babel-preset
```

</TabItem>
<TabItem value="npm" label="NPM">

```shell
npm install --save-dev @griffel/babel-preset
```

</TabItem>
</Tabs>

## Usage

Modify `.babelrc` to include:

```json
{
"presets": ["@griffel"]
}
```

## Configuration

Please check [the README](https://github.com/microsoft/griffel/tree/main/packages/babel-preset) of `@griffel/babel-preset` to check how to configure module evaluation and imports.
Loading

0 comments on commit f49dbca

Please sign in to comment.