Repository: microsoft/griffel
Pull request #175 (2022-07-27 15:47:35)

feat(devtools): open original source in source tab
PR #142 introduced the ability to open makeStyles call in source tab when clicking on '</>' button in section title. However it opens the compiled source. This PR enhance it to open real source. It should work with any build configuration (hopefully 🤞).

Before (can only open compiled source): 
![before](https://user-images.githubusercontent.com/28751745/181290753-02b9ecee-656f-44e8-80b1-56793be753d6.gif)


Now (open real source): 
![after](https://user-images.githubusercontent.com/28751745/181290859-f78a4bb7-1d27-477a-bec6-10f4e8f65fcc.gif)

 

github-actions[bot]: ## 📊 Bundle size report

<details>
<summary>Unchanged fixtures</summary>

| Package & Exports                                                                                                            |        Size (minified/GZIP) |
| ---------------------------------------------------------------------------------------------------------------------------- | --------------------------: |
| <samp>core</samp> <br /> <abbr title='bundle-size/makeStyles.fixture.js'>makeStyles + mergeClasses (build time)</abbr>       |       `1.8 kB`<br />`861 B` |
| <samp>core</samp> <br /> <abbr title='bundle-size/makeStylesRuntime.fixture.js'>makeStyles + mergeClasses (runtime)</abbr>   | `20.339 kB`<br />`7.598 kB` |
| <samp>react</samp> <br /> <abbr title='bundle-size/__css.fixture.js'>\_\_css + mergeClasses (build time)</abbr>              |     `1.837 kB`<br />`857 B` |
| <samp>react</samp> <br /> <abbr title='bundle-size/__styles.fixture.js'>\_\_styles + mergeClasses (build time)</abbr>        |  `3.535 kB`<br />`1.595 kB` |
| <samp>react</samp> <br /> <abbr title='bundle-size/makeStaticStyles-runtime.fixture.js'>makeStaticStyles (runtime)</abbr>    |  `8.982 kB`<br />`3.908 kB` |
| <samp>react</samp> <br /> <abbr title='bundle-size/makeStyles-runtime.fixture.js'>makeStyles + mergeClasses (runtime)</abbr> | `22.099 kB`<br />`8.317 kB` |

</details>
<sub>🤖 This report was generated against <a href='https://github.com/microsoft/griffel/commit/cdfb4ef879cfff15a61dd295239a25f7a6f0bdc1'>cdfb4ef879cfff15a61dd295239a25f7a6f0bdc1</a></sub>

<!-- Sticky Pull Request Comment -->

Patch:
diff --git a/.yarn/patches/source-map-js-npm-1.0.2-ee4f9f9b30.patch b/.yarn/patches/source-map-js-npm-1.0.2-ee4f9f9b30.patch
new file mode 100644
index 0000000..4d77f02
--- /dev/null
+++ b/.yarn/patches/source-map-js-npm-1.0.2-ee4f9f9b30.patch
@@ -0,0 +1,63 @@
+diff --git a/lib/quick-sort.js b/lib/quick-sort.js
+index 23f9eda57f7383b5291361e4593cf2fdef5409ba..016785171b4d69d756ac795763abc6cf2c9d1b31 100644
+--- a/lib/quick-sort.js
++++ b/lib/quick-sort.js
+@@ -15,8 +15,6 @@
+ // sorting in C++. By using our own JS-implemented Quick Sort (below), we get
+ // a ~3500ms mean speed-up in `bench/bench.html`.
+ 
+-function SortTemplate(comparator) {
+-
+ /**
+  * Swap the elements indexed by `x` and `y` in the array `ary`.
+  *
+@@ -42,7 +40,7 @@ function swap(ary, x, y) {
+  *        The upper bound on the range.
+  */
+ function randomIntInRange(low, high) {
+-  return Math.round(low + (Math.random() * (high - low)));
++  return Math.round(low + Math.random() * (high - low));
+ }
+ 
+ /**
+@@ -87,7 +85,7 @@ function doQuickSort(ary, comparator, p, r) {
+     //
+     //   * Every element in `ary[i+1 .. j-1]` is greater than the pivot.
+     for (var j = p; j < r; j++) {
+-      if (comparator(ary[j], pivot, false) <= 0) {
++      if (comparator(ary[j], pivot) <= 0) {
+         i += 1;
+         swap(ary, i, j);
+       }
+@@ -103,15 +101,6 @@ function doQuickSort(ary, comparator, p, r) {
+   }
+ }
+ 
+-  return doQuickSort;
+-}
+-
+-function cloneSort(comparator) {
+-  let template = SortTemplate.toString();
+-  let templateFn = new Function(`return ${template}`)();
+-  return templateFn(comparator);
+-}
+-
+ /**
+  * Sort the given array in-place with the given comparator function.
+  *
+@@ -120,13 +109,6 @@ function cloneSort(comparator) {
+  * @param {function} comparator
+  *        Function to use to compare two items.
+  */
+-
+-let sortCache = new WeakMap();
+-exports.quickSort = function (ary, comparator, start = 0) {
+-  let doQuickSort = sortCache.get(comparator);
+-  if (doQuickSort === void 0) {
+-    doQuickSort = cloneSort(comparator);
+-    sortCache.set(comparator, doQuickSort);
+-  }
+-  doQuickSort(ary, comparator, start, ary.length - 1);
++exports.quickSort = function (ary, comparator) {
++  doQuickSort(ary, comparator, 0, ary.length - 1);
+ };
diff --git a/apps/website/docs/react/api/shorthands.md b/apps/website/docs/react/api/shorthands.md
index 397f16e..b7f0398 100644
--- a/apps/website/docs/react/api/shorthands.md
+++ b/apps/website/docs/react/api/shorthands.md
@@ -224,3 +224,22 @@ const useClasses = makeStyles({
   },
 });
 ```
+
+### `shorthands.transition`
+
+```js
+import { makeStyles, shorthands } from '@griffel/react';
+
+const useClasses = makeStyles({
+  root: {
+    ...shorthands.transition('inherit'),
+    ...shorthands.transition('margin-right', '2s'),
+    ...shorthands.transition('margin-right', '4s', '1s'),
+    ...shorthands.transition('margin-right', '4s', '1s', 'ease-in'),
+    ...shorthands.transition([
+      ['margin-right', '4s', '1s', 'ease-in'],
+      ['margin-left', '2s', '0s', 'ease-in-out'],
+    ]),
+  },
+});
+```
diff --git a/change/@griffel-core-cca9a4d2-a460-4d77-a750-b6dd381c37f5.json b/change/@griffel-core-cca9a4d2-a460-4d77-a750-b6dd381c37f5.json
new file mode 100644
index 0000000..f5ef2ea
--- /dev/null
+++ b/change/@griffel-core-cca9a4d2-a460-4d77-a750-b6dd381c37f5.json
@@ -0,0 +1,7 @@
+{
+  "type": "minor",
+  "comment": "feat: add transition shorthand",
+  "packageName": "@griffel/core",
+  "email": "bernardo.sunderhus@gmail.com",
+  "dependentChangeType": "patch"
+}
diff --git a/change/@griffel-devtools-bf9f79e9-2ba1-41e8-8d1b-d4def78a15ba.json b/change/@griffel-devtools-bf9f79e9-2ba1-41e8-8d1b-d4def78a15ba.json
new file mode 100644
index 0000000..48f8022
--- /dev/null
+++ b/change/@griffel-devtools-bf9f79e9-2ba1-41e8-8d1b-d4def78a15ba.json
@@ -0,0 +1,7 @@
+{
+  "type": "minor",
+  "comment": "feat: 'inspect source' button opens real source instead of the compiled source",
+  "packageName": "@griffel/devtools",
+  "email": "yuanboxue@microsoft.com",
+  "dependentChangeType": "minor"
+}
diff --git a/package.json b/package.json
index 005777a..88b96ba 100644
--- a/package.json
+++ b/package.json
@@ -27,9 +27,9 @@
   "devDependencies": {
     "@babel/preset-typescript": "7.12.13",
     "@codesandbox/sandpack-react": "0.13.15",
-    "@docusaurus/core": "2.0.0-rc.1",
-    "@docusaurus/module-type-aliases": "^2.0.0-rc.1",
-    "@docusaurus/preset-classic": "2.0.0-rc.1",
+    "@docusaurus/core": "2.0.1",
+    "@docusaurus/module-type-aliases": "2.0.1",
+    "@docusaurus/preset-classic": "2.0.1",
     "@emotion/css": "^11.9.0",
     "@nrwl/cli": "13.4.5",
     "@nrwl/eslint-plugin-nx": "13.4.5",
@@ -86,12 +86,14 @@
     "eslint-plugin-react-hooks": "4.3.0",
     "highlight.js": "11.4.0",
     "jest": "27.2.3",
+    "jest-chrome": "^0.7.2",
     "js-beautify": "^1.14.0",
     "log-symbols": "5.1.0",
     "mini-css-extract-plugin": "2.6.0",
     "monosize": "0.0.3",
     "monosize-storage-upstash": "0.0.2",
     "nano-staged": "0.5.0",
+    "next": "^12.0.5",
     "prettier": "2.3.1",
     "prism-react-renderer": "1.2.1",
     "react": "17.0.2",
@@ -116,11 +118,13 @@
     "@linaria/shaker": "^3.0.0-beta.22",
     "@typescript-eslint/utils": "^5.20.0",
     "ajv": "^8.4.0",
+    "browserslist": "^4.19.1",
     "csstype": "^3.0.10",
     "enhanced-resolve": "^5.8.2",
     "loader-utils": "^2.0.0",
     "rtl-css-js": "^1.16.0",
     "schema-utils": "^3.1.1",
+    "source-map-js": "1.0.2",
     "stylis": "^4.0.13",
     "tslib": "^2.1.0"
   },
@@ -128,6 +132,7 @@
     "@nrwl/web": "patch:@nrwl/web@npm:13.4.5#.yarn/patches/@nrwl-web-npm-13.4.5-d7e9ea40d2",
     "@nrwl/workspace": "patch:@nrwl/workspace@npm:13.4.5#.yarn/patches/@nrwl-workspace-npm-13.4.5-233a3ec85c",
     "beachball": "patch:beachball@npm:2.21.0#.yarn/patches/beachball-npm-2.21.0-85c8ba3d6b",
-    "monosize@0.0.3": "patch:monosize@npm:0.0.3#.yarn/patches/monosize-npm-0.0.3-9b650c3f51"
+    "monosize@0.0.3": "patch:monosize@npm:0.0.3#.yarn/patches/monosize-npm-0.0.3-9b650c3f51",
+    "source-map-js@1.0.2": "patch:source-map-js@npm:1.0.2#.yarn/patches/source-map-js-npm-1.0.2-ee4f9f9b30.patch"
   }
 }
diff --git a/packages/babel-preset/CHANGELOG.json b/packages/babel-preset/CHANGELOG.json
index aa3e0b7..5c8ddb3 100644
--- a/packages/babel-preset/CHANGELOG.json
+++ b/packages/babel-preset/CHANGELOG.json
@@ -1,6 +1,21 @@
 {
   "name": "@griffel/babel-preset",
   "entries": [
+    {
+      "date": "Wed, 03 Aug 2022 13:28:05 GMT",
+      "tag": "@griffel/babel-preset_v1.3.6",
+      "version": "1.3.6",
+      "comments": {
+        "patch": [
+          {
+            "author": "olfedias@microsoft.com",
+            "package": "@griffel/babel-preset",
+            "commit": "7eb59c4270491c9220f45e7109d17ea6099697f5",
+            "comment": "fix: assets handling in Windows env"
+          }
+        ]
+      }
+    },
     {
       "date": "Wed, 27 Jul 2022 13:03:12 GMT",
       "tag": "@griffel/babel-preset_v1.3.5",
diff --git a/packages/babel-preset/CHANGELOG.md b/packages/babel-preset/CHANGELOG.md
index 73c558a..324e85e 100644
--- a/packages/babel-preset/CHANGELOG.md
+++ b/packages/babel-preset/CHANGELOG.md
@@ -1,9 +1,17 @@
 # Change Log - @griffel/babel-preset
 
-This log was last generated on Wed, 27 Jul 2022 13:03:12 GMT and should not be manually modified.
+This log was last generated on Wed, 03 Aug 2022 13:28:05 GMT and should not be manually modified.
 
 <!-- Start content -->
 
+## 1.3.6
+
+Wed, 03 Aug 2022 13:28:05 GMT
+
+### Patches
+
+- fix: assets handling in Windows env (olfedias@microsoft.com)
+
 ## 1.3.5
 
 Wed, 27 Jul 2022 13:03:12 GMT
diff --git a/packages/babel-preset/package.json b/packages/babel-preset/package.json
index 4ae66da..5652131 100644
--- a/packages/babel-preset/package.json
+++ b/packages/babel-preset/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@griffel/babel-preset",
-  "version": "1.3.5",
+  "version": "1.3.6",
   "description": "Babel preset with build time transforms for Griffel",
   "license": "MIT",
   "repository": {
diff --git a/packages/babel-preset/src/assets/absolutePathToRelative.ts b/packages/babel-preset/src/assets/absolutePathToRelative.ts
new file mode 100644
index 0000000..b27f6bc
--- /dev/null
+++ b/packages/babel-preset/src/assets/absolutePathToRelative.ts
@@ -0,0 +1,21 @@
+export function absolutePathToRelative(
+  path: typeof import('path'),
+  projectRoot: string,
+  filename: string,
+  assetPath: string,
+) {
+  const fileDirectory = path.dirname(filename);
+
+  const absoluteAssetPath = path.resolve(projectRoot, assetPath);
+  const assetDirectory = path.dirname(absoluteAssetPath);
+
+  if (fileDirectory === assetDirectory) {
+    return './' + path.basename(assetPath);
+  }
+
+  const relativeAssetPath = path.relative(fileDirectory, absoluteAssetPath);
+
+  // Normalize paths to be POSIX-like as bundlers don't handle Windows paths
+  // "path.posix" does not make sense there as there is no "windows-to-posix-path" function
+  return relativeAssetPath.split(path.sep).join(path.posix.sep);
+}
diff --git a/packages/babel-preset/src/assets/normalizeStyleRules.ts b/packages/babel-preset/src/assets/normalizeStyleRules.ts
index f47038a..d768ed3 100644
--- a/packages/babel-preset/src/assets/normalizeStyleRules.ts
+++ b/packages/babel-preset/src/assets/normalizeStyleRules.ts
@@ -1,18 +1,23 @@
 import { GriffelStyle } from '@griffel/core';
-import * as path from 'path';
 import { parseStringWithUrl } from './parseStringWithUrl';
 
 /**
  * Linaria v3 emits relative paths for assets, we normalize these paths to be relative from the project root to be the
  * same if an assets was used in different files.
  */
-export function normalizeAssetPath(projectRoot: string, filename: string, asset: string) {
-  const absolutePath = path.resolve(path.dirname(filename), asset);
+export function normalizeAssetPath(path: typeof import('path'), projectRoot: string, filename: string, asset: string) {
+  const absoluteAssetPath = path.resolve(path.dirname(filename), asset);
 
-  return path.relative(projectRoot, absolutePath);
+  // Normalize paths to be POSIX-like to be independent of an execution environment
+  return path.relative(projectRoot, absoluteAssetPath).split(path.sep).join(path.posix.sep);
 }
 
-export function normalizeStyleRule(projectRoot: string, filename: string, ruleValue: string | number) {
+export function normalizeStyleRule(
+  path: typeof import('path'),
+  projectRoot: string,
+  filename: string,
+  ruleValue: string | number,
+) {
   if (typeof ruleValue === 'number' || ruleValue.indexOf('url(') === -1) {
     return ruleValue;
   }
@@ -26,10 +31,11 @@ export function normalizeStyleRule(projectRoot: string, filename: string, ruleVa
     return `${result.prefix}${url}${result.suffix}`;
   }
 
-  return `${result.prefix}${normalizeAssetPath(projectRoot, filename, url)}${result.suffix}`;
+  return `${result.prefix}${normalizeAssetPath(path, projectRoot, filename, url)}${result.suffix}`;
 }
 
 export function normalizeStyleRules(
+  path: typeof import('path'),
   projectRoot: string,
   filename: string,
   stylesBySlots: Record<string /* slot */, GriffelStyle> | GriffelStyle,
@@ -42,15 +48,15 @@ export function normalizeStyleRules(
 
       // Fallback value
       if (Array.isArray(value)) {
-        return [key, value.map(rule => normalizeStyleRule(projectRoot, filename, rule as string))];
+        return [key, value.map(rule => normalizeStyleRule(path, projectRoot, filename, rule as string))];
       }
 
       // Nested objects
       if (typeof value === 'object') {
-        return [key, normalizeStyleRules(projectRoot, filename, value as unknown as GriffelStyle)];
+        return [key, normalizeStyleRules(path, projectRoot, filename, value as unknown as GriffelStyle)];
       }
 
-      return [key, normalizeStyleRule(projectRoot, filename, value)];
+      return [key, normalizeStyleRule(path, projectRoot, filename, value)];
     }),
   );
 }
diff --git a/packages/babel-preset/src/assets/replaceAssetsWithImports.ts b/packages/babel-preset/src/assets/replaceAssetsWithImports.ts
index 6bd2591..6ab8ce7 100644
--- a/packages/babel-preset/src/assets/replaceAssetsWithImports.ts
+++ b/packages/babel-preset/src/assets/replaceAssetsWithImports.ts
@@ -1,21 +1,9 @@
 import { NodePath, traverse, types as t } from '@babel/core';
 import * as path from 'path';
 
+import { absolutePathToRelative } from './absolutePathToRelative';
 import { parseStringWithUrl } from './parseStringWithUrl';
 
-export function absolutePathToRelative(projectRoot: string, filename: string, assetPath: string) {
-  const fileDirectory = path.dirname(filename);
-
-  const absoluteAssetPath = path.resolve(projectRoot, assetPath);
-  const assetDirectory = path.dirname(absoluteAssetPath);
-
-  if (fileDirectory === assetDirectory) {
-    return './' + path.basename(assetPath);
-  }
-
-  return path.relative(fileDirectory, absoluteAssetPath);
-}
-
 /**
  * Replaces assets used in styles with imports and template literals.
  *
@@ -67,7 +55,7 @@ export function replaceAssetsWithImports(
   );
 
   for (const [importPath, identifier] of assetIdentifiers.entries()) {
-    const relativePath = absolutePathToRelative(projectRoot, filename, importPath);
+    const relativePath = absolutePathToRelative(path, projectRoot, filename, importPath);
 
     programPath.unshiftContainer(
       'body',
diff --git a/packages/babel-preset/src/transformPlugin.ts b/packages/babel-preset/src/transformPlugin.ts
index a978d76..f02cd80 100644
--- a/packages/babel-preset/src/transformPlugin.ts
+++ b/packages/babel-preset/src/transformPlugin.ts
@@ -3,6 +3,7 @@ import { declare } from '@babel/helper-plugin-utils';
 import { Module } from '@linaria/babel-preset';
 import shakerEvaluator from '@linaria/shaker';
 import { resolveStyleRulesForSlots, CSSRulesByBucket, StyleBucketName, GriffelStyle } from '@griffel/core';
+import * as path from 'path';
 
 import { normalizeStyleRules } from './assets/normalizeStyleRules';
 import { replaceAssetsWithImports } from './assets/replaceAssetsWithImports';
@@ -185,6 +186,7 @@ export const transformPlugin = declare<Partial<BabelPluginOptions>, PluginObj<Ba
                 // Style rules should be normalized *before* they will be resolved to CSS rules to have deterministic
                 // results across different build targets.
                 normalizeStyleRules(
+                  path,
                   pluginOptions.projectRoot,
                   // Presence of "state.filename" is validated on `Program.enter()`
                   state.filename as string,
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index de694f7..aa817db 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -18,6 +18,7 @@ import {
   overflow,
   inset,
   outline,
+  transition,
 } from './shorthands/index';
 
 export const shorthands = {
@@ -38,6 +39,7 @@ export const shorthands = {
   overflow,
   inset,
   outline,
+  transition,
 };
 
 export { createDOMRenderer } from './renderer/createDOMRenderer';
diff --git a/packages/core/src/shorthands/index.ts b/packages/core/src/shorthands/index.ts
index aec15e7..1c21249 100644
--- a/packages/core/src/shorthands/index.ts
+++ b/packages/core/src/shorthands/index.ts
@@ -15,3 +15,4 @@ export { padding } from './padding';
 export { overflow } from './overflow';
 export { inset } from './inset';
 export { outline } from './outline';
+export { transition } from './transition';
diff --git a/packages/core/src/shorthands/transition.ts b/packages/core/src/shorthands/transition.ts
new file mode 100644
index 0000000..d4c38be
--- /dev/null
+++ b/packages/core/src/shorthands/transition.ts
@@ -0,0 +1,95 @@
+import type { GriffelStylesStrictCSSObject } from '../types';
+import {
+  TransitionDelayInput,
+  TransitionDurationInput,
+  TransitionPropertyInput,
+  TransitionTimingFunctionInput,
+  TransitionGlobalInput,
+} from './types';
+
+type TransitionInputs = [
+  TransitionPropertyInput,
+  TransitionDurationInput?,
+  TransitionDelayInput?,
+  TransitionTimingFunctionInput?,
+];
+
+type TransitionStyle = Pick<
+  GriffelStylesStrictCSSObject,
+  'transitionProperty' | 'transitionDelay' | 'transitionDuration' | 'transitionTimingFunction'
+>;
+
+export function transition(globalValue: TransitionGlobalInput): TransitionStyle;
+export function transition(property: TransitionPropertyInput, duration: TransitionDurationInput): TransitionStyle;
+export function transition(
+  property: TransitionPropertyInput,
+  duration: TransitionDurationInput,
+  delay: TransitionDelayInput,
+): TransitionStyle;
+export function transition(
+  property: TransitionPropertyInput,
+  duration: TransitionDurationInput,
+  delay: TransitionDelayInput,
+  easingFunction: TransitionTimingFunctionInput,
+): TransitionStyle;
+export function transition(values: TransitionInputs[]): TransitionStyle;
+
+/**
+ * A function that implements expansion for "transition", it's simplified - check usage examples.
+ *
+ * @example
+ *  transition('inherit')
+ *  transition('margin-right', '4s')
+ *  transition('margin-right', '4s', '1s')
+ *  transition('margin-right', '4s', '1s', 'ease-in')
+ *  transition([
+ *    ['margin-right', '4s', '1s', 'ease-in'],
+ *    ['margin-right', '4s', '1s', 'ease-in'],
+ * ])
+ *
+ * See https://developer.mozilla.org/en-US/docs/Web/CSS/transition
+ */
+export function transition(
+  ...values: [TransitionGlobalInput] | TransitionInputs | [TransitionInputs[]]
+): TransitionStyle {
+  if (isTransitionGlobalInputs(values)) {
+    return {
+      transitionDelay: values[0],
+      transitionDuration: values[0],
+      transitionProperty: values[0],
+      transitionTimingFunction: values[0],
+    };
+  }
+  const transitionInputs = normalizeTransitionInputs(values);
+  return transitionInputs.reduce<TransitionStyle>(
+    (acc, [property, duration = '0s', delay = '0s', timingFunction = 'ease'], index) => {
+      if (index === 0) {
+        acc.transitionProperty = property;
+        acc.transitionDuration = duration;
+        acc.transitionDelay = delay;
+        acc.transitionTimingFunction = timingFunction;
+      } else {
+        acc.transitionProperty += `, ${property}`;
+        acc.transitionDuration += `, ${duration}`;
+        acc.transitionDelay += `, ${delay}`;
+        acc.transitionTimingFunction += `, ${timingFunction}`;
+      }
+      return acc;
+    },
+    {},
+  );
+}
+
+const transitionGlobalInputs: TransitionGlobalInput[] = ['-moz-initial', 'inherit', 'initial', 'revert', 'unset'];
+function isTransitionGlobalInputs(
+  values: [TransitionGlobalInput] | TransitionInputs | [TransitionInputs[]],
+): values is [TransitionGlobalInput] {
+  return values.length === 1 && transitionGlobalInputs.includes(values[0] as TransitionGlobalInput);
+}
+
+function normalizeTransitionInputs(transitionInputs: TransitionInputs | [TransitionInputs[]]): TransitionInputs[] {
+  if (transitionInputs.length === 1 && Array.isArray(transitionInputs[0])) {
+    return transitionInputs[0];
+  }
+  return [transitionInputs as TransitionInputs];
+}
diff --git a/packages/core/src/shorthands/types.ts b/packages/core/src/shorthands/types.ts
index 22e37c1..0d6dc05 100644
--- a/packages/core/src/shorthands/types.ts
+++ b/packages/core/src/shorthands/types.ts
@@ -24,3 +24,9 @@ export type PaddingInput = ValueOrArray<CSS.Property.Padding<GriffelStylesCSSVal
 export type InsetInput = ValueOrArray<CSS.Property.Inset<GriffelStylesCSSValue>>;
 
 export type FlexInput = [CSS.Property.Flex, CSS.Property.Flex?, CSS.Property.Flex?];
+
+export type TransitionPropertyInput = CSS.Property.TransitionProperty;
+export type TransitionDurationInput = CSS.Property.TransitionDuration;
+export type TransitionDelayInput = CSS.Property.TransitionDelay;
+export type TransitionTimingFunctionInput = CSS.Property.TransitionTimingFunction;
+export type TransitionGlobalInput = CSS.Globals;
diff --git a/packages/devtools/CHANGELOG.json b/packages/devtools/CHANGELOG.json
index 86474b9..37d5bb0 100644
--- a/packages/devtools/CHANGELOG.json
+++ b/packages/devtools/CHANGELOG.json
@@ -1,6 +1,21 @@
 {
   "name": "@griffel/devtools",
   "entries": [
+    {
+      "date": "Wed, 03 Aug 2022 15:28:36 GMT",
+      "tag": "@griffel/devtools_v0.1.1",
+      "version": "0.1.1",
+      "comments": {
+        "patch": [
+          {
+            "author": "beachball",
+            "package": "@griffel/devtools",
+            "comment": "Bump @griffel/react to v1.3.0",
+            "commit": "5fe534790f489fc865785463f716b196d5db0cd1"
+          }
+        ]
+      }
+    },
     {
       "date": "Wed, 27 Jul 2022 13:03:12 GMT",
       "tag": "@griffel/devtools_v0.1.0",
diff --git a/packages/devtools/CHANGELOG.md b/packages/devtools/CHANGELOG.md
index 6f62871..3d5d489 100644
--- a/packages/devtools/CHANGELOG.md
+++ b/packages/devtools/CHANGELOG.md
@@ -1,9 +1,17 @@
 # Change Log - @griffel/devtools
 
-This log was last generated on Wed, 27 Jul 2022 13:03:12 GMT and should not be manually modified.
+This log was last generated on Wed, 03 Aug 2022 15:28:36 GMT and should not be manually modified.
 
 <!-- Start content -->
 
+## 0.1.1
+
+Wed, 03 Aug 2022 15:28:36 GMT
+
+### Patches
+
+- Bump @griffel/react to v1.3.0
+
 ## 0.1.0
 
 Wed, 27 Jul 2022 13:03:12 GMT
diff --git a/packages/devtools/jest.config.js b/packages/devtools/jest.config.js
index 82d8b18..a3021ab 100644
--- a/packages/devtools/jest.config.js
+++ b/packages/devtools/jest.config.js
@@ -6,4 +6,5 @@ module.exports = {
   },
   moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
   coverageDirectory: '../../coverage/packages/devtools',
+  setupFilesAfterEnv: ['./jest.setup.js'],
 };
diff --git a/packages/devtools/jest.setup.js b/packages/devtools/jest.setup.js
new file mode 100644
index 0000000..0e1c31d
--- /dev/null
+++ b/packages/devtools/jest.setup.js
@@ -0,0 +1,13 @@
+// eslint-disable-next-line import/no-extraneous-dependencies
+Object.assign(global, require('jest-chrome'));
+
+chrome.devtools.inspectedWindow.getResources.mockImplementation(cb => {
+  cb([
+    {
+      url: 'webpack://testscope/root/packages/test1/src/App.styles.js?42f2',
+      type: 'sm-script',
+      getContent: () => '',
+      setContent: () => ({}),
+    },
+  ]);
+});
diff --git a/packages/devtools/package.json b/packages/devtools/package.json
index f19c459..0952d29 100644
--- a/packages/devtools/package.json
+++ b/packages/devtools/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@griffel/devtools",
-  "version": "0.1.0",
+  "version": "0.1.1",
   "description": "Griffel chrome devtools extension",
   "license": "MIT",
   "repository": {
@@ -10,11 +10,12 @@
   "sideEffects": false,
   "dependencies": {
     "@emotion/hash": "^0.8.0",
-    "@griffel/react": "^1.2.3",
+    "@griffel/react": "^1.3.0",
     "js-beautify": "^1.14.0",
     "prism-react-renderer": "1.2.1",
     "react-dom": ">=16.8.0 <18.0.0",
     "react": ">=16.8.0 <18.0.0",
+    "source-map-js": "1.0.2",
     "stylis": "^4.0.13"
   }
 }
diff --git a/packages/devtools/src/SlotCSSRules.tsx b/packages/devtools/src/SlotCSSRules.tsx
index 96eb22a..f54b2c5 100644
--- a/packages/devtools/src/SlotCSSRules.tsx
+++ b/packages/devtools/src/SlotCSSRules.tsx
@@ -3,6 +3,7 @@ import { makeStyles, mergeClasses, shorthands } from '@griffel/react';
 
 import { getMonolithicCSSRules } from './getMonolithicCSSRules';
 import { MonolithicRulesView } from './MonolithicRulesView';
+import { loadOriginalSourceLoc } from './sourceMap';
 import { tokens } from './themes';
 import { useViewContext } from './ViewContext';
 
@@ -93,12 +94,13 @@ export const SlotCSSRules: React.FC<{ slot: string; atomicRules: AtomicRules[];
   );
 };
 
-function openOriginalCode(sourceURL: string) {
-  chrome.devtools.inspectedWindow.eval<string>('window.location.origin', {}, async () => {
-    // example sourceURL: https://source.js:<lineNumber>:<columnNumber>
-    const results = sourceURL.split(':');
-    results.pop();
-    const line = Number(results.pop()) ?? 1;
-    chrome.devtools.panels.openResource(results.join(':'), line - 1, () => ({}));
-  });
+/**
+ * Opens source in chrome source tab
+ *
+ * @param sourceUrlWithLoc source url with line and column
+ * example: https://source.js:<lineNumber>:<columnNumber>
+ */
+async function openOriginalCode(sourceUrlWithLoc: string) {
+  const originalPosition = await loadOriginalSourceLoc(sourceUrlWithLoc);
+  chrome.devtools.panels.openResource(originalPosition.source, originalPosition.line - 1, () => ({}));
 }
diff --git a/packages/devtools/src/sourceMap/index.ts b/packages/devtools/src/sourceMap/index.ts
new file mode 100644
index 0000000..4c079ac
--- /dev/null
+++ b/packages/devtools/src/sourceMap/index.ts
@@ -0,0 +1,1 @@
+export { loadOriginalSourceLoc } from './loadOriginalSourceLoc';
diff --git a/packages/devtools/src/sourceMap/loadOriginalSourceLoc.ts b/packages/devtools/src/sourceMap/loadOriginalSourceLoc.ts
new file mode 100644
index 0000000..e4e8095
--- /dev/null
+++ b/packages/devtools/src/sourceMap/loadOriginalSourceLoc.ts
@@ -0,0 +1,181 @@
+import type { MappedPosition, RawSourceMap } from 'source-map-js';
+import { getFilePath, getOriginalPosition, resources } from './sourceMapConsumer';
+
+// when only one source map appears in a source file: source url -> source map JSON
+// when more than one source maps appear in a source file: `${source}:${line}:${column}` -> source map JSON
+const sourceMapJSONs: Map<string, RawSourceMap> = new Map();
+
+function getComputedSourceMapJSON({ source, line, column }: MappedPosition) {
+  return sourceMapJSONs.get(source) ?? sourceMapJSONs.get(`${source}:${line}:${column}`);
+}
+
+export async function loadOriginalSourceLoc(sourceUrlWithLoc: string): Promise<MappedPosition> {
+  const sourceLoc = parseSourceUrl(sourceUrlWithLoc);
+  if (sourceLoc === null) {
+    return { source: sourceUrlWithLoc, line: 1, column: 0 };
+  }
+
+  const sourceMapJSON = getComputedSourceMapJSON(sourceLoc);
+  if (sourceMapJSON) {
+    return getOriginalPosition(sourceMapJSON, sourceLoc);
+  }
+
+  try {
+    return extractAndLoadSourceMapJSON(sourceLoc);
+  } catch (error) {
+    console.warn(`[Griffel devtools] unable to load source map for ${sourceUrlWithLoc}`);
+    console.warn(error);
+    return sourceLoc;
+  }
+}
+
+function parseSourceUrl(sourceUrlWithLoc: string): MappedPosition | null {
+  const paths = sourceUrlWithLoc.split(':');
+  const column = Number(paths.pop());
+  const line = Number(paths.pop());
+  const source = paths.join(':');
+  if (Number.isNaN(line) || Number.isNaN(column)) {
+    return null;
+  }
+  return { source, line, column };
+}
+
+// inspired by https://github.com/facebook/react/blob/b66936ece7d9ad41a33e077933c9af0bda8bff87/packages/react-devtools-shared/src/hooks/parseHookNames/loadSourceAndMetadata.js#L135
+async function extractAndLoadSourceMapJSON(sourceLoc: MappedPosition): Promise<MappedPosition> {
+  const { source, line, column } = sourceLoc;
+
+  const runtimeSourceCode = await fetchFiles(source);
+
+  const externalSourceMapURLs = [];
+  let sourceMapNum = 0;
+  const sourceMapRegex = / ?sourceMappingURL=([^\s'"]+)/gm;
+
+  let sourceMappingURLMatch = sourceMapRegex.exec(runtimeSourceCode);
+  if (!sourceMappingURLMatch) {
+    throw new Error(`[Griffel devtools] extractAndLoadSourceMapJSON() no source map found in ${source}`);
+  }
+
+  while (sourceMappingURLMatch) {
+    sourceMapNum++;
+    const sourceMappingURL = sourceMappingURLMatch[1];
+
+    const hasInlineSourceMap = sourceMappingURL.indexOf('base64,') >= 0;
+    if (hasInlineSourceMap) {
+      try {
+        // Web apps like Code Sandbox embed multiple inline source maps.
+        // In this case, we need to loop through and find the right one.
+        const trimmed = sourceMappingURL.match(/base64,([a-zA-Z0-9+/=]+)/)?.[1] ?? '';
+        const decoded = atob(trimmed);
+        const sourceMapJSON = JSON.parse(decoded);
+
+        // TODO optionally turn on debug message
+        // if (__DEBUG__) {
+        //   console.groupCollapsed('[Griffel devtools] extractAndLoadSourceMapJSON() Inline source map');
+        //   console.log(sourceMapJSON);
+        //   console.groupEnd();
+        // }
+
+        if (sourceMapIncludesSource(sourceMapJSON, getFilePath(source))) {
+          // store computed sourceMapJSON
+          if (sourceMapNum > 1) {
+            sourceMapJSONs.set(`${source}:${line}:${column}`, sourceMapJSON);
+          } else {
+            if (sourceMapRegex.exec(runtimeSourceCode) === null) {
+              sourceMapJSONs.set(source, sourceMapJSON);
+            } else {
+              sourceMapJSONs.set(`${source}:${line}:${column}`, sourceMapJSON);
+            }
+          }
+
+          return getOriginalPosition(sourceMapJSON, sourceLoc);
+        }
+      } catch (error) {
+        // We've likely encountered a string in the source code that looks like a source map but isn't.
+        // Maybe the source code contains a "sourceMappingURL" comment or something similar.
+        // In either case, let's skip this and keep looking.
+      }
+    } else {
+      externalSourceMapURLs.push(sourceMappingURL);
+    }
+
+    sourceMappingURLMatch = sourceMapRegex.exec(runtimeSourceCode);
+  }
+
+  // No inline sourcemap found. Check if there's external source map
+  let sourceMappingURL = externalSourceMapURLs.pop();
+  if (!sourceMappingURL) {
+    throw new Error(
+      `[Griffel devtools] extractAndLoadSourceMapJSON() encountered string "sourceMappingURL" in file ${source} that does not contain a valid source map`,
+    );
+  }
+
+  if (externalSourceMapURLs.length > 0) {
+    // Files with external source maps should only have a single source map.
+    // More than one result might indicate an edge case,
+    // like a string in the source code that matched our "sourceMappingURL" regex.
+    // We should just skip over cases like this.
+    console.warn(
+      `[Griffel devtools] extractAndLoadSourceMapJSON() More than one external source map detected in the file ${source}:`,
+    );
+    externalSourceMapURLs.forEach(sourceMappingURL => {
+      console.warn(`[Griffel devtools] extractAndLoadSourceMapJSON() skipping source map "${sourceMappingURL}"`);
+    });
+  }
+
+  if (!sourceMappingURL.startsWith('http') && !sourceMappingURL.startsWith('/')) {
+    const lastSlashIdx = source.lastIndexOf('/');
+    if (lastSlashIdx !== -1) {
+      const baseURL = source.slice(0, lastSlashIdx);
+      sourceMappingURL = `${baseURL}/${sourceMappingURL}`;
+    }
+  }
+
+  const sourceMapContent = await fetchFiles(sourceMappingURL);
+  const sourceMapJSON = JSON.parse(sourceMapContent);
+  sourceMapJSONs.set(source, sourceMapJSON);
+
+  return getOriginalPosition(sourceMapJSON, sourceLoc);
+}
+
+async function fetchFiles(url: string): Promise<string> {
+  try {
+    const response = await fetch(url);
+    if (response.ok) {
+      return response.text();
+    }
+    throw new Error(`[Griffel devtools] fetchRuntimeSource() bad response fetching ${url}: ${response.status}`);
+  } catch (error) {
+    // try to get url content from chrome resources api
+    const resource = (await resources).find(resource => resource.url === url);
+    if (resource) {
+      return new Promise(resolve => resource.getContent(content => resolve(content)));
+    }
+    throw error;
+  }
+}
+
+type IndexSourceMapSection = {
+  map: IndexSourceMap | RawSourceMap;
+  offset: {
+    line: number;
+    column: number;
+  };
+};
+type IndexSourceMap = {
+  file?: string;
+  mappings?: void;
+  sourcesContent?: void;
+  sections: IndexSourceMapSection[];
+  version: number;
+};
+function isRawSourceMap(sourcemap: RawSourceMap | IndexSourceMap): sourcemap is RawSourceMap {
+  return sourcemap.mappings !== undefined;
+}
+function sourceMapIncludesSource(sourcemap: RawSourceMap | IndexSourceMap, sourcePath: string): boolean {
+  if (isRawSourceMap(sourcemap)) {
+    return sourcemap.sources.some(s => s === 'Inline Babel script' || s.includes(sourcePath));
+  }
+  return sourcemap.sections.some(section => {
+    return sourceMapIncludesSource(section.map, sourcePath);
+  });
+}
diff --git a/packages/devtools/src/sourceMap/sourceMapConsumer.ts b/packages/devtools/src/sourceMap/sourceMapConsumer.ts
new file mode 100644
index 0000000..6459ead
--- /dev/null
+++ b/packages/devtools/src/sourceMap/sourceMapConsumer.ts
@@ -0,0 +1,45 @@
+import { SourceMapConsumer, MappedPosition, RawSourceMap } from 'source-map-js';
+
+export const resources: Promise<chrome.devtools.inspectedWindow.Resource[]> = new Promise(resolve => {
+  chrome.devtools.inspectedWindow.getResources(currResources => {
+    resolve(currResources);
+  });
+});
+chrome.devtools.inspectedWindow.onResourceAdded.addListener(async resource => {
+  if (!resource.url.startsWith('debugger')) {
+    (await resources).push(resource);
+  }
+});
+
+export async function getOriginalPosition(sourceMapJSON: RawSourceMap, sourceLoc: MappedPosition) {
+  const { source, line, column } = sourceLoc;
+  try {
+    const sourceMapConsumer = new SourceMapConsumer(sourceMapJSON);
+    const result = sourceMapConsumer.originalPositionFor({ line, column });
+
+    // The url in source map is not always the url used in chrome source tab.
+    // We find the real url by searching the path of url in chrome resources.
+    let originalSource = result.source;
+    const path = getFilePath(result.source);
+    if (path) {
+      originalSource = (await resources).find(resource => resource.url.includes(path))?.url ?? originalSource;
+    }
+
+    return { ...result, source: originalSource };
+  } catch (error) {
+    console.error(`[Griffel devtools] unable to consume source map for ${source}:${line}:${column}`);
+    console.error(error);
+    return sourceLoc;
+  }
+}
+
+/**
+ * @param url
+ * @returns path of the url, for example:
+ * webpack-interal://src/a -> src/a
+ * webpack://@test/pkg/src/a -> test/pkg/src/a
+ * /pkg/src/a -> pkg/src/a
+ */
+export function getFilePath(url: string) {
+  return url.match(/^([^:]+:\/\/|)([./@]*)(.*)$/)?.[3] ?? '';
+}
diff --git a/packages/next-extraction-plugin/.eslintrc.json b/packages/next-extraction-plugin/.eslintrc.json
new file mode 100644
index 0000000..9d9c0db
--- /dev/null
+++ b/packages/next-extraction-plugin/.eslintrc.json
@@ -0,0 +1,18 @@
+{
+  "extends": ["../../.eslintrc.json"],
+  "ignorePatterns": ["!**/*"],
+  "overrides": [
+    {
+      "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
+      "rules": {}
+    },
+    {
+      "files": ["*.ts", "*.tsx"],
+      "rules": {}
+    },
+    {
+      "files": ["*.js", "*.jsx"],
+      "rules": {}
+    }
+  ]
+}
diff --git a/packages/next-extraction-plugin/CHANGELOG.json b/packages/next-extraction-plugin/CHANGELOG.json
new file mode 100644
index 0000000..94ce066
--- /dev/null
+++ b/packages/next-extraction-plugin/CHANGELOG.json
@@ -0,0 +1,26 @@
+{
+  "name": "@griffel/next-extraction-plugin",
+  "entries": [
+    {
+      "date": "Fri, 05 Aug 2022 09:44:44 GMT",
+      "tag": "@griffel/next-extraction-plugin_v0.0.2",
+      "version": "0.0.2",
+      "comments": {
+        "patch": [
+          {
+            "author": "dwlad90@gmail.com",
+            "package": "@griffel/next-extraction-plugin",
+            "commit": "81c9176d20ecefdeca14c94fb4705563f5faeba2",
+            "comment": "feat: export GriffelCSSExtractionPluginOptions type"
+          },
+          {
+            "author": "beachball",
+            "package": "@griffel/next-extraction-plugin",
+            "comment": "Bump @griffel/webpack-extraction-plugin to v0.1.5",
+            "commit": "81c9176d20ecefdeca14c94fb4705563f5faeba2"
+          }
+        ]
+      }
+    }
+  ]
+}
diff --git a/packages/next-extraction-plugin/CHANGELOG.md b/packages/next-extraction-plugin/CHANGELOG.md
new file mode 100644
index 0000000..ee37344
--- /dev/null
+++ b/packages/next-extraction-plugin/CHANGELOG.md
@@ -0,0 +1,14 @@
+# Change Log - @griffel/next-extraction-plugin
+
+This log was last generated on Fri, 05 Aug 2022 09:44:44 GMT and should not be manually modified.
+
+<!-- Start content -->
+
+## 0.0.2
+
+Fri, 05 Aug 2022 09:44:44 GMT
+
+### Patches
+
+- feat: export GriffelCSSExtractionPluginOptions type (dwlad90@gmail.com)
+- Bump @griffel/webpack-extraction-plugin to v0.1.5
diff --git a/packages/next-extraction-plugin/README.md b/packages/next-extraction-plugin/README.md
new file mode 100644
index 0000000..caa2c37
--- /dev/null
+++ b/packages/next-extraction-plugin/README.md
@@ -0,0 +1,35 @@
+
+# Next plugin to perform CSS extraction in Griffel
+
+A plugin for NextJS 12.0.5 and newer that add [`@griffel/webpack-extraction-plugin`](../webpack-extraction-plugin) to webpack loaders pipeline.
+
+## Install
+
+```bash
+yarn add --dev @griffel/next-extraction-plugin
+# or
+npm install --save-dev @griffel/next-extraction-plugin
+```
+
+## Usage
+
+In `next.config.js` file you'll need to add the next-plugin from `@griffel/webpack-extraction-plugin` like so:
+```js
+// next.config.js
+const { withGriffelCSSExtraction } = require('@griffel/webpack-extraction-plugin');
+
+module.exports = withGriffelCssExtraction()({
+  webpack(config) {
+    config.module.rules.push({
+      test: /\.(js|jsx|tsx|ts)$/,
+      exclude: /node_modules/,
+      use: [
+        {
+          loader: '@griffel/webpack-loader',
+        },
+      ],
+    });
+    return config;
+  },
+});
+```
diff --git a/packages/next-extraction-plugin/jest.config.js b/packages/next-extraction-plugin/jest.config.js
new file mode 100644
index 0000000..9fd510d
--- /dev/null
+++ b/packages/next-extraction-plugin/jest.config.js
@@ -0,0 +1,15 @@
+module.exports = {
+  displayName: 'next-extraction-plugin',
+  preset: '../../jest.preset.js',
+  globals: {
+    'ts-jest': {
+      tsconfig: '<rootDir>/tsconfig.spec.json',
+    },
+  },
+  testEnvironment: 'node',
+  transform: {
+    '^.+\\.[tj]sx?$': 'ts-jest',
+  },
+  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
+  coverageDirectory: '../../coverage/packages/next-extraction-plugin',
+};
diff --git a/packages/next-extraction-plugin/package.json b/packages/next-extraction-plugin/package.json
new file mode 100644
index 0000000..90f9120
--- /dev/null
+++ b/packages/next-extraction-plugin/package.json
@@ -0,0 +1,19 @@
+{
+  "name": "@griffel/next-extraction-plugin",
+  "version": "0.0.2",
+  "description": "NextJS plugin that add Griffel's CSS extraction to webpack loaders pipeline",
+  "license": "MIT",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/microsoft/griffel"
+  },
+  "dependencies": {
+    "@griffel/webpack-extraction-plugin": "^0.1.5",
+    "browserslist": "^4.19.1"
+  },
+  "peerDependencies": {
+    "@griffel/core": "^1.5.1",
+    "next": "^12.0.5",
+    "webpack": "^5"
+  }
+}
diff --git a/packages/next-extraction-plugin/project.json b/packages/next-extraction-plugin/project.json
new file mode 100644
index 0000000..4e83b1f
--- /dev/null
+++ b/packages/next-extraction-plugin/project.json
@@ -0,0 +1,42 @@
+{
+  "root": "packages/next-extraction-plugin",
+  "sourceRoot": "packages/next-extraction-plugin/src",
+  "projectType": "library",
+  "targets": {
+    "lint": {
+      "executor": "@nrwl/linter:eslint",
+      "outputs": ["{options.outputFile}"],
+      "options": {
+        "lintFilePatterns": ["packages/next-extraction-plugin/**/*.ts"]
+      }
+    },
+    "test": {
+      "executor": "@nrwl/jest:jest",
+      "outputs": ["coverage/packages/next-extraction-plugin"],
+      "options": {
+        "jestConfig": "packages/next-extraction-plugin/jest.config.js",
+        "passWithNoTests": true
+      }
+    },
+    "build": {
+      "executor": "@nrwl/node:package",
+      "outputs": ["{options.outputPath}"],
+      "options": {
+        "outputPath": "dist/packages/next-extraction-plugin",
+        "tsConfig": "packages/next-extraction-plugin/tsconfig.lib.json",
+        "packageJson": "packages/next-extraction-plugin/package.json",
+        "main": "packages/next-extraction-plugin/src/index.ts",
+        "assets": ["packages/next-extraction-plugin/*.md"]
+      }
+    },
+    "type-check": {
+      "executor": "@nrwl/workspace:run-commands",
+      "options": {
+        "cwd": "packages/next-extraction-plugin",
+        "commands": [{ "command": "tsc -b --pretty" }],
+        "outputPath": []
+      }
+    }
+  },
+  "tags": []
+}
diff --git a/packages/next-extraction-plugin/src/index.ts b/packages/next-extraction-plugin/src/index.ts
new file mode 100644
index 0000000..a2e4df8
--- /dev/null
+++ b/packages/next-extraction-plugin/src/index.ts
@@ -0,0 +1,1 @@
+export { withGriffelCSSExtraction } from './lib/next-extraction-plugin';
diff --git a/packages/next-extraction-plugin/src/lib/next-extraction-plugin.spec.ts b/packages/next-extraction-plugin/src/lib/next-extraction-plugin.spec.ts
new file mode 100644
index 0000000..42cb9d2
--- /dev/null
+++ b/packages/next-extraction-plugin/src/lib/next-extraction-plugin.spec.ts
@@ -0,0 +1,126 @@
+import { withGriffelCSSExtraction } from './next-extraction-plugin';
+
+import type { NextConfig } from 'next';
+import type { WebpackConfigContext } from 'next/dist/server/config-shared';
+import type { Configuration, RuleSetRule } from 'webpack';
+
+const getNextConfig: () => NextConfig = () => ({
+  webpack: config => {
+    return config;
+  },
+});
+
+const getWebpackConfig: () => Configuration = () => ({
+  module: {
+    rules: [
+      {
+        oneOf: [
+          {
+            test: /.css/,
+          },
+        ],
+      },
+    ],
+  },
+  plugins: [],
+});
+
+const options = {
+  defaultLoaders: {},
+  dev: false,
+  dir: '/',
+  isServer: true,
+  buildId: 'griffel',
+} as WebpackConfigContext;
+
+describe('nextExtractionPlugin', () => {
+  it('should work', () => {
+    const config = withGriffelCSSExtraction({})({});
+
+    expect(config).toBeInstanceOf(Object);
+    expect(config.webpack).toBeInstanceOf(Function);
+  });
+
+  it('should failed with next@5 and lower', () => {
+    const config = withGriffelCSSExtraction({})({});
+
+    if (config?.webpack) {
+      const result = () => config.webpack?.({}, {} as WebpackConfigContext);
+      expect(result).toThrow(Error);
+    }
+  });
+
+  it('should call custom webpack config', () => {
+    // eslint-disable-next-line @typescript-eslint/no-empty-function
+    const nextConfig = getNextConfig();
+    const consoleSpy = jest.spyOn(nextConfig, 'webpack');
+
+    const config = withGriffelCSSExtraction({})(nextConfig);
+
+    if (config?.webpack) config?.webpack({}, options);
+
+    expect(consoleSpy).toBeCalled();
+  });
+
+  it('should extract css when mode is prod', () => {
+    const nextConfig = getNextConfig();
+    const webpackConfig = getWebpackConfig();
+
+    const config = withGriffelCSSExtraction({})(nextConfig);
+
+    expect(webpackConfig.plugins?.length).toEqual(0);
+    expect(webpackConfig.module?.rules?.length).toEqual(1);
+    expect((webpackConfig.module?.rules?.[0] as RuleSetRule)?.oneOf?.length).toEqual(1);
+
+    config.webpack?.(webpackConfig, { ...options });
+
+    expect(webpackConfig.plugins?.length).toEqual(1);
+    expect(webpackConfig.module?.rules?.length).toEqual(2);
+    expect((webpackConfig.module?.rules?.[1] as RuleSetRule)?.oneOf?.length).toEqual(2);
+  });
+
+  it('should extend webpack loader rule', () => {
+    const nextConfig = getNextConfig();
+    const webpackConfig = getWebpackConfig();
+
+    // @ts-expect-error - pass 'test' param isn't legal
+    const config = withGriffelCSSExtraction({ extractLoaderRuleAttrs: { test: /\.(jsx)$/, exclude: /node_modules/ } })(
+      nextConfig,
+    );
+
+    config.webpack?.(webpackConfig, { ...options });
+
+    const rule = webpackConfig.module?.rules?.[0] as RuleSetRule;
+
+    expect(rule?.exclude).toEqual(/node_modules/);
+    expect(rule?.test).toEqual(/\.(tsx|ts|js|jsx)$/);
+    expect(rule?.use).toHaveLength(1);
+  });
+
+  it("should't extract css when mode is dev", () => {
+    const nextConfig = getNextConfig();
+    const webpackConfig = getWebpackConfig();
+
+    const config = withGriffelCSSExtraction({})(nextConfig);
+
+    config.webpack?.(webpackConfig, { ...options, dev: true });
+
+    expect(webpackConfig.plugins?.length).toEqual(0);
+    expect(webpackConfig.module?.rules?.length).toEqual(1);
+    expect((webpackConfig.module?.rules?.[0] as RuleSetRule)?.oneOf?.length).toEqual(1);
+  });
+
+  it("should't extract css if native css loader isn't exist", () => {
+    const nextConfig = getNextConfig();
+    const webpackConfig = getWebpackConfig();
+
+    webpackConfig.module!.rules!.length = 0;
+
+    const config = withGriffelCSSExtraction({})(nextConfig);
+
+    config.webpack?.(webpackConfig, { ...options, dev: true });
+
+    expect(webpackConfig.plugins?.length).toEqual(0);
+    expect(webpackConfig.module?.rules?.length).toEqual(0);
+  });
+});
diff --git a/packages/next-extraction-plugin/src/lib/next-extraction-plugin.ts b/packages/next-extraction-plugin/src/lib/next-extraction-plugin.ts
new file mode 100644
index 0000000..eab354d
--- /dev/null
+++ b/packages/next-extraction-plugin/src/lib/next-extraction-plugin.ts
@@ -0,0 +1,107 @@
+import { GriffelCSSExtractionPlugin } from '@griffel/webpack-extraction-plugin';
+import * as browserslist from 'browserslist';
+import { lazyPostCSS } from 'next/dist/build/webpack/config/blocks/css';
+import { getGlobalCssLoader } from 'next/dist/build/webpack/config/blocks/css/loaders';
+
+import type { GriffelCSSExtractionPluginOptions } from '@griffel/webpack-extraction-plugin';
+import type { NextConfig } from 'next';
+import type { ConfigurationContext } from 'next/dist/build/webpack/config/utils';
+import type { Configuration, RuleSetRule } from 'webpack';
+
+type GriffelNextExtractionPluginOptions = GriffelCSSExtractionPluginOptions & {
+  extractLoaderRuleAttrs?: Omit<RuleSetRule, 'test' | 'use'>;
+};
+
+function getSupportedBrowsers(dir: string, isDevelopment: boolean) {
+  let browsers: string[] | undefined;
+  try {
+    browsers = browserslist.loadConfig({
+      path: dir,
+      env: isDevelopment ? 'development' : 'production',
+    });
+  } catch {
+    // Prevent error on build time
+  }
+
+  return browsers;
+}
+
+export const withGriffelCSSExtraction =
+  ({
+    extractLoaderRuleAttrs: webpackLoaderRules = {},
+    ...webpackPluginOptions
+  }: GriffelNextExtractionPluginOptions = {}) =>
+  (nextConfig: NextConfig = {}) =>
+    Object.assign({}, nextConfig, {
+      webpack(config: Configuration & ConfigurationContext, options) {
+        const { dir, dev, isServer } = options;
+
+        if (!options.defaultLoaders) {
+          throw new Error(
+            'This plugin is not compatible with Next.js versions below 5.0.0 https://err.sh/next-plugins/upgrade',
+          );
+        }
+
+        // The @Griffel compiler must run on source code, which means it must be
+        // configured as the last loader in webpack so that it runs before any
+        // other transformation.
+
+        if (typeof nextConfig.webpack === 'function') {
+          // eslint-disable-next-line no-param-reassign
+          config = nextConfig.webpack(config, options);
+        }
+
+        if (dev) {
+          // not push @griffel/webpack-extraction-plugin if dev build
+          return config;
+        }
+
+        const cssRules = (
+          config.module?.rules?.find(
+            rule =>
+              typeof rule === 'object' &&
+              Array.isArray(rule.oneOf) &&
+              rule.oneOf.some(
+                ({ test }) => test instanceof RegExp && typeof test.test === 'function' && test.test('filename.css'),
+              ),
+          ) as RuleSetRule
+        )?.oneOf;
+
+        if (cssRules) {
+          config?.module?.rules?.unshift({
+            ...webpackLoaderRules,
+            test: /\.(tsx|ts|js|jsx)$/,
+            use: [
+              {
+                loader: GriffelCSSExtractionPlugin.loader,
+              },
+            ],
+          });
+
+          cssRules.unshift({
+            test: /griffel\.css$/i,
+            sideEffects: true,
+            use: getGlobalCssLoader(
+              {
+                assetPrefix: config.assetPrefix,
+                isClient: !isServer,
+                isServer,
+                isDevelopment: dev,
+                future: nextConfig.future || {},
+                experimental: nextConfig.experimental || {},
+              } as ConfigurationContext,
+              () => lazyPostCSS(dir, getSupportedBrowsers(dir, dev), undefined),
+              [],
+            ),
+          });
+
+          if (!Array.isArray(config?.plugins)) {
+            config.plugins = [];
+          }
+
+          config.plugins.push(new GriffelCSSExtractionPlugin(webpackPluginOptions));
+        }
+
+        return config;
+      },
+    } as NextConfig);
diff --git a/packages/next-extraction-plugin/tsconfig.json b/packages/next-extraction-plugin/tsconfig.json
new file mode 100644
index 0000000..0639863
--- /dev/null
+++ b/packages/next-extraction-plugin/tsconfig.json
@@ -0,0 +1,24 @@
+{
+  "extends": "../../tsconfig.base.json",
+  "compilerOptions": {
+    "allowJs": true,
+    "checkJs": true,
+    "lib": ["es2019", "dom", "DOM.Iterable"],
+    "forceConsistentCasingInFileNames": true,
+    "strict": true,
+    "noImplicitOverride": true,
+    "noPropertyAccessFromIndexSignature": true,
+    "noImplicitReturns": true,
+    "noFallthroughCasesInSwitch": true
+  },
+  "files": [],
+  "include": [],
+  "references": [
+    {
+      "path": "./tsconfig.lib.json"
+    },
+    {
+      "path": "./tsconfig.spec.json"
+    }
+  ]
+}
diff --git a/packages/next-extraction-plugin/tsconfig.lib.json b/packages/next-extraction-plugin/tsconfig.lib.json
new file mode 100644
index 0000000..8d27d94
--- /dev/null
+++ b/packages/next-extraction-plugin/tsconfig.lib.json
@@ -0,0 +1,11 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "module": "commonjs",
+    "outDir": "../../dist/out-tsc",
+    "declaration": true,
+    "types": ["node", "environment"]
+  },
+  "exclude": ["**/*.spec.ts", "**/*.test.ts"],
+  "include": ["**/*.ts"]
+}
diff --git a/packages/next-extraction-plugin/tsconfig.spec.json b/packages/next-extraction-plugin/tsconfig.spec.json
new file mode 100644
index 0000000..2db12f1
--- /dev/null
+++ b/packages/next-extraction-plugin/tsconfig.spec.json
@@ -0,0 +1,21 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "jsx": "react",
+    "outDir": "../../dist/out-tsc",
+    "module": "commonjs",
+    "types": ["jest", "node", "environment", "static-assets"]
+  },
+  "include": [
+    "__fixtures__/**/*/code.ts",
+    "**/*.test.ts",
+    "**/*.spec.ts",
+    "**/*.test.tsx",
+    "**/*.spec.tsx",
+    "**/*.test.js",
+    "**/*.spec.js",
+    "**/*.test.jsx",
+    "**/*.spec.jsx",
+    "**/*.d.ts"
+  ]
+}
diff --git a/packages/react/CHANGELOG.json b/packages/react/CHANGELOG.json
index b7142f7..b29e878 100644
--- a/packages/react/CHANGELOG.json
+++ b/packages/react/CHANGELOG.json
@@ -1,6 +1,21 @@
 {
   "name": "@griffel/react",
   "entries": [
+    {
+      "date": "Wed, 03 Aug 2022 15:28:36 GMT",
+      "tag": "@griffel/react_v1.3.0",
+      "version": "1.3.0",
+      "comments": {
+        "minor": [
+          {
+            "author": "olfedias@microsoft.com",
+            "package": "@griffel/react",
+            "commit": "5fe534790f489fc865785463f716b196d5db0cd1",
+            "comment": "feat: re-export GriffelRenderer type"
+          }
+        ]
+      }
+    },
     {
       "date": "Wed, 27 Jul 2022 13:03:12 GMT",
       "tag": "@griffel/react_v1.2.3",
diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md
index fb83ad0..d1462c4 100644
--- a/packages/react/CHANGELOG.md
+++ b/packages/react/CHANGELOG.md
@@ -1,9 +1,17 @@
 # Change Log - @griffel/react
 
-This log was last generated on Wed, 27 Jul 2022 13:03:12 GMT and should not be manually modified.
+This log was last generated on Wed, 03 Aug 2022 15:28:36 GMT and should not be manually modified.
 
 <!-- Start content -->
 
+## 1.3.0
+
+Wed, 03 Aug 2022 15:28:36 GMT
+
+### Minor changes
+
+- feat: re-export GriffelRenderer type (olfedias@microsoft.com)
+
 ## 1.2.3
 
 Wed, 27 Jul 2022 13:03:12 GMT
diff --git a/packages/react/README.md b/packages/react/README.md
index 8cdf9aa..098867d 100644
--- a/packages/react/README.md
+++ b/packages/react/README.md
@@ -248,6 +248,25 @@ const useClasses = makeStyles({
 });
 ```
 
+### `shorthands.transition`
+
+```js
+import { makeStyles, shorthands } from '@griffel/react';
+
+const useClasses = makeStyles({
+  root: {
+    ...shorthands.transition('inherit'),
+    ...shorthands.transition('margin-right', '2s'),
+    ...shorthands.transition('margin-right', '4s', '1s'),
+    ...shorthands.transition('margin-right', '4s', '1s', 'ease-in'),
+    ...shorthands.transition([
+      ['margin-right', '4s', '1s', 'ease-in'],
+      ['margin-left', '2s', '0s', 'ease-in-out'],
+    ]),
+  },
+});
+```
+
 ## `makeStaticStyles()`
 
 Creates styles attached to a global selector. Styles can be defined via objects:
diff --git a/packages/react/package.json b/packages/react/package.json
index a22a65d..325d0a7 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@griffel/react",
-  "version": "1.2.3",
+  "version": "1.3.0",
   "description": "React implementation of Atomic CSS-in-JS",
   "license": "MIT",
   "repository": {
diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts
index 5cdceae..b75982c 100644
--- a/packages/react/src/index.ts
+++ b/packages/react/src/index.ts
@@ -1,5 +1,5 @@
 export { shorthands, mergeClasses, createDOMRenderer } from '@griffel/core';
-export type { GriffelStyle, CreateDOMRendererOptions } from '@griffel/core';
+export type { GriffelStyle, CreateDOMRendererOptions, GriffelRenderer } from '@griffel/core';
 
 export { makeStyles } from './makeStyles';
 export { makeStaticStyles } from './makeStaticStyles';
diff --git a/packages/webpack-extraction-plugin/CHANGELOG.json b/packages/webpack-extraction-plugin/CHANGELOG.json
index bd0d145..cd126ec 100644
--- a/packages/webpack-extraction-plugin/CHANGELOG.json
+++ b/packages/webpack-extraction-plugin/CHANGELOG.json
@@ -1,6 +1,36 @@
 {
   "name": "@griffel/webpack-extraction-plugin",
   "entries": [
+    {
+      "date": "Fri, 05 Aug 2022 09:44:44 GMT",
+      "tag": "@griffel/webpack-extraction-plugin_v0.1.5",
+      "version": "0.1.5",
+      "comments": {
+        "patch": [
+          {
+            "author": "dwlad90@gmail.com",
+            "package": "@griffel/webpack-extraction-plugin",
+            "commit": "81c9176d20ecefdeca14c94fb4705563f5faeba2",
+            "comment": "feat: add NextJS plugin for CSS extraction"
+          }
+        ]
+      }
+    },
+    {
+      "date": "Wed, 03 Aug 2022 13:28:05 GMT",
+      "tag": "@griffel/webpack-extraction-plugin_v0.1.4",
+      "version": "0.1.4",
+      "comments": {
+        "patch": [
+          {
+            "author": "olfedias@microsoft.com",
+            "package": "@griffel/webpack-extraction-plugin",
+            "commit": "7eb59c4270491c9220f45e7109d17ea6099697f5",
+            "comment": "fix: assets handling in Windows env"
+          }
+        ]
+      }
+    },
     {
       "date": "Wed, 27 Jul 2022 13:03:12 GMT",
       "tag": "@griffel/webpack-extraction-plugin_v0.1.3",
diff --git a/packages/webpack-extraction-plugin/CHANGELOG.md b/packages/webpack-extraction-plugin/CHANGELOG.md
index bbc2d0d..4f3ebd5 100644
--- a/packages/webpack-extraction-plugin/CHANGELOG.md
+++ b/packages/webpack-extraction-plugin/CHANGELOG.md
@@ -1,9 +1,25 @@
 # Change Log - @griffel/webpack-extraction-plugin
 
-This log was last generated on Wed, 27 Jul 2022 13:03:12 GMT and should not be manually modified.
+This log was last generated on Fri, 05 Aug 2022 09:44:44 GMT and should not be manually modified.
 
 <!-- Start content -->
 
+## 0.1.5
+
+Fri, 05 Aug 2022 09:44:44 GMT
+
+### Patches
+
+- feat: add NextJS plugin for CSS extraction (dwlad90@gmail.com)
+
+## 0.1.4
+
+Wed, 03 Aug 2022 13:28:05 GMT
+
+### Patches
+
+- fix: assets handling in Windows env (olfedias@microsoft.com)
+
 ## 0.1.3
 
 Wed, 27 Jul 2022 13:03:12 GMT
diff --git a/packages/webpack-extraction-plugin/package.json b/packages/webpack-extraction-plugin/package.json
index fc3509f..aac5b4e 100644
--- a/packages/webpack-extraction-plugin/package.json
+++ b/packages/webpack-extraction-plugin/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@griffel/webpack-extraction-plugin",
-  "version": "0.1.3",
+  "version": "0.1.5",
   "description": "Webpack plugin that performs CSS extraction for Griffel",
   "license": "MIT",
   "repository": {
diff --git a/packages/webpack-extraction-plugin/src/GriffelCSSExtractionPlugin.ts b/packages/webpack-extraction-plugin/src/GriffelCSSExtractionPlugin.ts
index 5d73692..a9de0f3 100644
--- a/packages/webpack-extraction-plugin/src/GriffelCSSExtractionPlugin.ts
+++ b/packages/webpack-extraction-plugin/src/GriffelCSSExtractionPlugin.ts
@@ -4,7 +4,7 @@ import type { Compiler, sources } from 'webpack';
 
 import { sortCSSRules } from './sortCSSRules';
 
-type GriffelCSSExtractionPluginOptions = {
+export type GriffelCSSExtractionPluginOptions = {
   compareMediaQueries?: GriffelRenderer['compareMediaQueries'];
 };
 
diff --git a/packages/webpack-extraction-plugin/src/babelPluginStripGriffelRuntime.ts b/packages/webpack-extraction-plugin/src/babelPluginStripGriffelRuntime.ts
index 8a8e52f..836f33e 100644
--- a/packages/webpack-extraction-plugin/src/babelPluginStripGriffelRuntime.ts
+++ b/packages/webpack-extraction-plugin/src/babelPluginStripGriffelRuntime.ts
@@ -21,7 +21,11 @@ export function transformUrl(filename: string, resourceDirectory: string, assetP
   const absoluteAssetPath = path.resolve(path.dirname(filename), assetPath);
 
   // Replace asset path with new path relative to the output CSS
-  return path.relative(resourceDirectory, absoluteAssetPath);
+  const relativeAssetPath = path.relative(resourceDirectory, absoluteAssetPath);
+
+  // Normalize paths to be POSIX-like as bundlers don't handle Windows paths
+  // "path.posix" does not make sense there as there is no "windows-to-posix-path" function
+  return relativeAssetPath.split(path.sep).join(path.posix.sep);
 }
 
 export const babelPluginStripGriffelRuntime = declare<
diff --git a/packages/webpack-extraction-plugin/src/index.ts b/packages/webpack-extraction-plugin/src/index.ts
index 6e1e6f2..ba565ed 100644
--- a/packages/webpack-extraction-plugin/src/index.ts
+++ b/packages/webpack-extraction-plugin/src/index.ts
@@ -1,1 +1,2 @@
 export { GriffelCSSExtractionPlugin } from './GriffelCSSExtractionPlugin';
+export type { GriffelCSSExtractionPluginOptions } from './GriffelCSSExtractionPlugin';
diff --git a/packages/webpack-loader/CHANGELOG.json b/packages/webpack-loader/CHANGELOG.json
index b2f3551..ba6e1dd 100644
--- a/packages/webpack-loader/CHANGELOG.json
+++ b/packages/webpack-loader/CHANGELOG.json
@@ -1,6 +1,36 @@
 {
   "name": "@griffel/webpack-loader",
   "entries": [
+    {
+      "date": "Wed, 03 Aug 2022 15:28:36 GMT",
+      "tag": "@griffel/webpack-loader_v2.0.12",
+      "version": "2.0.12",
+      "comments": {
+        "patch": [
+          {
+            "author": "beachball",
+            "package": "@griffel/webpack-loader",
+            "comment": "Bump @griffel/react to v1.3.0",
+            "commit": "5fe534790f489fc865785463f716b196d5db0cd1"
+          }
+        ]
+      }
+    },
+    {
+      "date": "Wed, 03 Aug 2022 13:28:05 GMT",
+      "tag": "@griffel/webpack-loader_v2.0.11",
+      "version": "2.0.11",
+      "comments": {
+        "patch": [
+          {
+            "author": "beachball",
+            "package": "@griffel/webpack-loader",
+            "comment": "Bump @griffel/babel-preset to v1.3.6",
+            "commit": "7eb59c4270491c9220f45e7109d17ea6099697f5"
+          }
+        ]
+      }
+    },
     {
       "date": "Wed, 27 Jul 2022 13:03:12 GMT",
       "tag": "@griffel/webpack-loader_v2.0.10",
diff --git a/packages/webpack-loader/CHANGELOG.md b/packages/webpack-loader/CHANGELOG.md
index b100a5a..ea45722 100644
--- a/packages/webpack-loader/CHANGELOG.md
+++ b/packages/webpack-loader/CHANGELOG.md
@@ -1,9 +1,25 @@
 # Change Log - @griffel/webpack-loader
 
-This log was last generated on Wed, 27 Jul 2022 13:03:12 GMT and should not be manually modified.
+This log was last generated on Wed, 03 Aug 2022 15:28:36 GMT and should not be manually modified.
 
 <!-- Start content -->
 
+## 2.0.12
+
+Wed, 03 Aug 2022 15:28:36 GMT
+
+### Patches
+
+- Bump @griffel/react to v1.3.0
+
+## 2.0.11
+
+Wed, 03 Aug 2022 13:28:05 GMT
+
+### Patches
+
+- Bump @griffel/babel-preset to v1.3.6
+
 ## 2.0.10
 
 Wed, 27 Jul 2022 13:03:12 GMT
diff --git a/packages/webpack-loader/package.json b/packages/webpack-loader/package.json
index 247c672..ba5586b 100644
--- a/packages/webpack-loader/package.json
+++ b/packages/webpack-loader/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@griffel/webpack-loader",
-  "version": "2.0.10",
+  "version": "2.0.12",
   "description": "Webpack loader that performs build time transforms for Griffel",
   "license": "MIT",
   "repository": {
@@ -9,14 +9,14 @@
   },
   "dependencies": {
     "@babel/core": "^7.12.13",
-    "@griffel/babel-preset": "^1.3.5",
+    "@griffel/babel-preset": "^1.3.6",
     "enhanced-resolve": "^5.8.2",
     "loader-utils": "^2.0.0",
     "schema-utils": "^3.1.1",
     "tslib": "^2.1.0"
   },
   "peerDependencies": {
-    "@griffel/react": "^1.2.3",
+    "@griffel/react": "^1.3.0",
     "webpack": "^5"
   }
 }
diff --git a/tsconfig.base.json b/tsconfig.base.json
index e607fba..113872b 100644
--- a/tsconfig.base.json
+++ b/tsconfig.base.json
@@ -21,8 +21,10 @@
       "@griffel/e2e-typescript": ["e2e/typescript/src/index.ts"],
       "@griffel/eslint-plugin": ["packages/eslint-plugin/src/index.ts"],
       "@griffel/jest-serializer": ["packages/jest-serializer/src/index.ts"],
+      "@griffel/next-extraction-plugin": ["packages/next-extraction-plugin/src/index.ts"],
       "@griffel/react": ["packages/react/src/index.ts"],
-      "@griffel/webpack-loader": ["packages/webpack-loader/src/index.ts"]
+      "@griffel/webpack-loader": ["packages/webpack-loader/src/index.ts"],
+      "@griffel/webpack-extraction-plugin": ["packages/webpack-extraction-plugin/src/index.ts"]
     },
     "typeRoots": ["node_modules/@types", "./typings"]
   },
diff --git a/workspace.json b/workspace.json
index 59a619b..59601ec 100644
--- a/workspace.json
+++ b/workspace.json
@@ -8,6 +8,7 @@
     "@griffel/e2e-typescript": "e2e/typescript",
     "@griffel/eslint-plugin": "packages/eslint-plugin",
     "@griffel/jest-serializer": "packages/jest-serializer",
+    "@griffel/next-extraction-plugin": "packages/next-extraction-plugin",
     "@griffel/react": "packages/react",
     "@griffel/webpack-loader": "packages/webpack-loader",
     "@griffel/webpack-extraction-plugin": "packages/webpack-extraction-plugin",
diff --git a/yarn.lock b/yarn.lock
index 1eb7ee1..1aba21b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2130,9 +2130,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@docusaurus/core@npm:2.0.0-rc.1":
-  version: 2.0.0-rc.1
-  resolution: "@docusaurus/core@npm:2.0.0-rc.1"
+"@docusaurus/core@npm:2.0.1":
+  version: 2.0.1
+  resolution: "@docusaurus/core@npm:2.0.1"
   dependencies:
     "@babel/core": ^7.18.6
     "@babel/generator": ^7.18.7
@@ -2144,13 +2144,13 @@ __metadata:
     "@babel/runtime": ^7.18.6
     "@babel/runtime-corejs3": ^7.18.6
     "@babel/traverse": ^7.18.8
-    "@docusaurus/cssnano-preset": 2.0.0-rc.1
-    "@docusaurus/logger": 2.0.0-rc.1
-    "@docusaurus/mdx-loader": 2.0.0-rc.1
+    "@docusaurus/cssnano-preset": 2.0.1
+    "@docusaurus/logger": 2.0.1
+    "@docusaurus/mdx-loader": 2.0.1
     "@docusaurus/react-loadable": 5.5.2
-    "@docusaurus/utils": 2.0.0-rc.1
-    "@docusaurus/utils-common": 2.0.0-rc.1
-    "@docusaurus/utils-validation": 2.0.0-rc.1
+    "@docusaurus/utils": 2.0.1
+    "@docusaurus/utils-common": 2.0.1
+    "@docusaurus/utils-validation": 2.0.1
     "@slorber/static-site-generator-webpack-plugin": ^4.0.7
     "@svgr/webpack": ^6.2.1
     autoprefixer: ^10.4.7
@@ -2210,40 +2210,40 @@ __metadata:
     react-dom: ^16.8.4 || ^17.0.0
   bin:
     docusaurus: bin/docusaurus.mjs
-  checksum: 45bb6fbb8464dd17a55e66011a208400b6932ab1dfd8e3ba000aa46cbbaf4a72a0f6d0020eff8f2236191fd7de19fd708a8d7a12317b6dd9b35857a687fec869
+  checksum: a5f5bdc96143fa7053c6de0f7e80922bb3e3df78120e9cd911aa311ddb2db6a7b5d28b6d3baebc256270637baba3dc4b9660c4117646cc3da3078b1e4e91780e
   languageName: node
   linkType: hard
 
-"@docusaurus/cssnano-preset@npm:2.0.0-rc.1":
-  version: 2.0.0-rc.1
-  resolution: "@docusaurus/cssnano-preset@npm:2.0.0-rc.1"
+"@docusaurus/cssnano-preset@npm:2.0.1":
+  version: 2.0.1
+  resolution: "@docusaurus/cssnano-preset@npm:2.0.1"
   dependencies:
     cssnano-preset-advanced: ^5.3.8
     postcss: ^8.4.14
     postcss-sort-media-queries: ^4.2.1
     tslib: ^2.4.0
-  checksum: e7e78594bb1b2bfe4ffd8dab609dd988835954820f4dd3d83a7733769cbd9fe7a03fa9d843511d24f42e476c0fbc7e1766c338b0b66ac97ba308c8534dfd136b
+  checksum: eeefba128aa6d7ddf68c443e6ab2d80360d6cc28993a1393be4ad75b366e208d7beb5cddac56c6bc36faa4cef7310013ba9a0dd40b639fff6ce254875f21ce79
   languageName: node
   linkType: hard
 
-"@docusaurus/logger@npm:2.0.0-rc.1":
-  version: 2.0.0-rc.1
-  resolution: "@docusaurus/logger@npm:2.0.0-rc.1"
+"@docusaurus/logger@npm:2.0.1":
+  version: 2.0.1
+  resolution: "@docusaurus/logger@npm:2.0.1"
   dependencies:
     chalk: ^4.1.2
     tslib: ^2.4.0
-  checksum: f31bb05e7e9ffc473253422173236a87a7f539298d720b7236aaabb8ea0790a303da8df8a1054c6ed35374e744c91754170d5431b1eeefc76346eb8395532e19
+  checksum: fa526efb87fb077415a7c5451591cf9afb3b8f327c6a9d6158fa020b85d02bf7d4af9cb808394de99bc67beea2d07690dd1922bf31460e8d953c576a231363d5
   languageName: node
   linkType: hard
 
-"@docusaurus/mdx-loader@npm:2.0.0-rc.1":
-  version: 2.0.0-rc.1
-  resolution: "@docusaurus/mdx-loader@npm:2.0.0-rc.1"
+"@docusaurus/mdx-loader@npm:2.0.1":
+  version: 2.0.1
+  resolution: "@docusaurus/mdx-loader@npm:2.0.1"
   dependencies:
     "@babel/parser": ^7.18.8
     "@babel/traverse": ^7.18.8
-    "@docusaurus/logger": 2.0.0-rc.1
-    "@docusaurus/utils": 2.0.0-rc.1
+    "@docusaurus/logger": 2.0.1
+    "@docusaurus/utils": 2.0.1
     "@mdx-js/mdx": ^1.6.22
     escape-html: ^1.0.3
     file-loader: ^6.2.0
@@ -2260,16 +2260,16 @@ __metadata:
   peerDependencies:
     react: ^16.8.4 || ^17.0.0
     react-dom: ^16.8.4 || ^17.0.0
-  checksum: 60f8c0b49a5abea245444d3aab4d11fe95a79c60c3171bd815dc6f0e99ba590c927f19622f43266b2f771d69838399ee7390ec4f7e94576d489e0687b6aa98f7
+  checksum: 2990ea2bba25c587875e327cbbb257da66cdcdb2041b2fcafb34ae44e4383be8e1b0cae38ee61e24400d86b61777f85abc4fdcd838c26c4ea86762bfcce2baf1
   languageName: node
   linkType: hard
 
-"@docusaurus/module-type-aliases@npm:2.0.0-rc.1, @docusaurus/module-type-aliases@npm:^2.0.0-rc.1":
-  version: 2.0.0-rc.1
-  resolution: "@docusaurus/module-type-aliases@npm:2.0.0-rc.1"
+"@docusaurus/module-type-aliases@npm:2.0.1":
+  version: 2.0.1
+  resolution: "@docusaurus/module-type-aliases@npm:2.0.1"
   dependencies:
     "@docusaurus/react-loadable": 5.5.2
-    "@docusaurus/types": 2.0.0-rc.1
+    "@docusaurus/types": 2.0.1
     "@types/history": ^4.7.11
     "@types/react": "*"
     "@types/react-router-config": "*"
@@ -2279,21 +2279,21 @@ __metadata:
   peerDependencies:
     react: "*"
     react-dom: "*"
-  checksum: 858d1919c51b47e43ee0c0d5742d4efd9971521ad2f446c8ada3c9e2cb3f567a0a356dd6c6c13bd7dd1bbdfa50fbbe5a108900b2d1a268735084210a8f30735a
+  checksum: 8cb9f21ec964d90dc12fa37b94eaa5d9610714438c20c8aca15bf836ba877a814289065a522190188a077daa19441d00af7b727a2633066d9dbf682894023a1f
   languageName: node
   linkType: hard
 
-"@docusaurus/plugin-content-blog@npm:2.0.0-rc.1":
-  version: 2.0.0-rc.1
-  resolution: "@docusaurus/plugin-content-blog@npm:2.0.0-rc.1"
-  dependencies:
-    "@docusaurus/core": 2.0.0-rc.1
-    "@docusaurus/logger": 2.0.0-rc.1
-    "@docusaurus/mdx-loader": 2.0.0-rc.1
-    "@docusaurus/types": 2.0.0-rc.1
-    "@docusaurus/utils": 2.0.0-rc.1
-    "@docusaurus/utils-common": 2.0.0-rc.1
-    "@docusaurus/utils-validation": 2.0.0-rc.1
+"@docusaurus/plugin-content-blog@npm:2.0.1":
+  version: 2.0.1
+  resolution: "@docusaurus/plugin-content-blog@npm:2.0.1"
+  dependencies:
+    "@docusaurus/core": 2.0.1
+    "@docusaurus/logger": 2.0.1
+    "@docusaurus/mdx-loader": 2.0.1
+    "@docusaurus/types": 2.0.1
+    "@docusaurus/utils": 2.0.1
+    "@docusaurus/utils-common": 2.0.1
+    "@docusaurus/utils-validation": 2.0.1
     cheerio: ^1.0.0-rc.12
     feed: ^4.2.2
     fs-extra: ^10.1.0
@@ -2306,21 +2306,21 @@ __metadata:
   peerDependencies:
     react: ^16.8.4 || ^17.0.0
     react-dom: ^16.8.4 || ^17.0.0
-  checksum: f7dd6576ef219522b52738c99c42ee0344875a3afed0615923fe09b48b1440ce297132076d712a3ab584aa3e8c7ddb164ecccb954e41bb500644b0a45940293b
+  checksum: 1ad52a1ea7c870f951853ffae733acb6303c272d1d5acb279aecedc4ee1fea74a0c32c581cd072250180352ebb541e41361e8553d986639e3ea938c29d69635f
   languageName: node
   linkType: hard
 
-"@docusaurus/plugin-content-docs@npm:2.0.0-rc.1":
-  version: 2.0.0-rc.1
-  resolution: "@docusaurus/plugin-content-docs@npm:2.0.0-rc.1"
-  dependencies:
-    "@docusaurus/core": 2.0.0-rc.1
-    "@docusaurus/logger": 2.0.0-rc.1
-    "@docusaurus/mdx-loader": 2.0.0-rc.1
-    "@docusaurus/module-type-aliases": 2.0.0-rc.1
-    "@docusaurus/types": 2.0.0-rc.1
-    "@docusaurus/utils": 2.0.0-rc.1
-    "@docusaurus/utils-validation": 2.0.0-rc.1
+"@docusaurus/plugin-content-docs@npm:2.0.1":
+  version: 2.0.1
+  resolution: "@docusaurus/plugin-content-docs@npm:2.0.1"
+  dependencies:
+    "@docusaurus/core": 2.0.1
+    "@docusaurus/logger": 2.0.1
+    "@docusaurus/mdx-loader": 2.0.1
+    "@docusaurus/module-type-aliases": 2.0.1
+    "@docusaurus/types": 2.0.1
+    "@docusaurus/utils": 2.0.1
+    "@docusaurus/utils-validation": 2.0.1
     "@types/react-router-config": ^5.0.6
     combine-promises: ^1.1.0
     fs-extra: ^10.1.0
@@ -2333,116 +2333,116 @@ __metadata:
   peerDependencies:
     react: ^16.8.4 || ^17.0.0
     react-dom: ^16.8.4 || ^17.0.0
-  checksum: b551c7935dcc2d03c68be6627a5120b5a1dc0e8d358b80cbba117ee55c6003371740be050aec056c2b4ad50d1b2ea8a4316ac2754b4a154e3310595ee62a7859
+  checksum: 508a8897d4785f85a5464796191e3e8e3f93c4e4cc0b77f7198020686b8e67e21660b9e88bf15509fabceaeb6d087ed51212e8c37e5dada9c87d83874575abcd
   languageName: node
   linkType: hard
 
-"@docusaurus/plugin-content-pages@npm:2.0.0-rc.1":
-  version: 2.0.0-rc.1
-  resolution: "@docusaurus/plugin-content-pages@npm:2.0.0-rc.1"
+"@docusaurus/plugin-content-pages@npm:2.0.1":
+  version: 2.0.1
+  resolution: "@docusaurus/plugin-content-pages@npm:2.0.1"
   dependencies:
-    "@docusaurus/core": 2.0.0-rc.1
-    "@docusaurus/mdx-loader": 2.0.0-rc.1
-    "@docusaurus/types": 2.0.0-rc.1
-    "@docusaurus/utils": 2.0.0-rc.1
-    "@docusaurus/utils-validation": 2.0.0-rc.1
+    "@docusaurus/core": 2.0.1
+    "@docusaurus/mdx-loader": 2.0.1
+    "@docusaurus/types": 2.0.1
+    "@docusaurus/utils": 2.0.1
+    "@docusaurus/utils-validation": 2.0.1
     fs-extra: ^10.1.0
     tslib: ^2.4.0
     webpack: ^5.73.0
   peerDependencies:
     react: ^16.8.4 || ^17.0.0
     react-dom: ^16.8.4 || ^17.0.0
-  checksum: 7f82fe83504534a96193666c7d19ccc4cc9c653bada390d8d084fc624c2be844eeaea4f6901ec9e69264eef7b90122cf962a0084742ad7afbfc71a9a6e91d39e
+  checksum: 5bbeb65096e557e9364469c1adaa8279e49e06f1068c2db52b9af44c7745c6e1284104ed6ec848d261d3024bd02550fa7984a15ae60201dcb8cc79f1044a49ae
   languageName: node
   linkType: hard
 
-"@docusaurus/plugin-debug@npm:2.0.0-rc.1":
-  version: 2.0.0-rc.1
-  resolution: "@docusaurus/plugin-debug@npm:2.0.0-rc.1"
+"@docusaurus/plugin-debug@npm:2.0.1":
+  version: 2.0.1
+  resolution: "@docusaurus/plugin-debug@npm:2.0.1"
   dependencies:
-    "@docusaurus/core": 2.0.0-rc.1
-    "@docusaurus/types": 2.0.0-rc.1
-    "@docusaurus/utils": 2.0.0-rc.1
+    "@docusaurus/core": 2.0.1
+    "@docusaurus/types": 2.0.1
+    "@docusaurus/utils": 2.0.1
     fs-extra: ^10.1.0
     react-json-view: ^1.21.3
     tslib: ^2.4.0
   peerDependencies:
     react: ^16.8.4 || ^17.0.0
     react-dom: ^16.8.4 || ^17.0.0
-  checksum: 7d781b5a3ff5b55909208a25d430ed3ee02b621111350e93d0f5962e429448f12dab0db9a8c1b084f02216acbda914cc3c77f672704030434a1abfeeb0b740f8
+  checksum: eefbea31ee84ccd523ee92d794ba53248dee2f9c606f503ff46456d467909bca8f5e90e8cf9f7afaeda846ece67233dd9807c0e0402d76e1ae9dbf661ec02948
   languageName: node
   linkType: hard
 
-"@docusaurus/plugin-google-analytics@npm:2.0.0-rc.1":
-  version: 2.0.0-rc.1
-  resolution: "@docusaurus/plugin-google-analytics@npm:2.0.0-rc.1"
+"@docusaurus/plugin-google-analytics@npm:2.0.1":
+  version: 2.0.1
+  resolution: "@docusaurus/plugin-google-analytics@npm:2.0.1"
   dependencies:
-    "@docusaurus/core": 2.0.0-rc.1
-    "@docusaurus/types": 2.0.0-rc.1
-    "@docusaurus/utils-validation": 2.0.0-rc.1
+    "@docusaurus/core": 2.0.1
+    "@docusaurus/types": 2.0.1
+    "@docusaurus/utils-validation": 2.0.1
     tslib: ^2.4.0
   peerDependencies:
     react: ^16.8.4 || ^17.0.0
     react-dom: ^16.8.4 || ^17.0.0
-  checksum: 92dfc0668735373bdd4d1e3f58f6df09445f5193f53d7b117d2a03fbf41918ba537e1f392b39724c5c6e441210118c0c88c9834b3684a86802a415cc4ad4eb75
+  checksum: 9d13b39e2db12390b6d01c14e039aa83869e1b53085e5b66ddaa818e2607ec2c5167fb66a9916d5fa3a59198c64e6d89d962aee88ac63b0923b4089755268da4
   languageName: node
   linkType: hard
 
-"@docusaurus/plugin-google-gtag@npm:2.0.0-rc.1":
-  version: 2.0.0-rc.1
-  resolution: "@docusaurus/plugin-google-gtag@npm:2.0.0-rc.1"
+"@docusaurus/plugin-google-gtag@npm:2.0.1":
+  version: 2.0.1
+  resolution: "@docusaurus/plugin-google-gtag@npm:2.0.1"
   dependencies:
-    "@docusaurus/core": 2.0.0-rc.1
-    "@docusaurus/types": 2.0.0-rc.1
-    "@docusaurus/utils-validation": 2.0.0-rc.1
+    "@docusaurus/core": 2.0.1
+    "@docusaurus/types": 2.0.1
+    "@docusaurus/utils-validation": 2.0.1
     tslib: ^2.4.0
   peerDependencies:
     react: ^16.8.4 || ^17.0.0
     react-dom: ^16.8.4 || ^17.0.0
-  checksum: 36590a4ef980775f504af857cd128709f0fde28a028a725657373a7bfbfb590b6bba7fc3b81d45299369e77486f221eca22df7643f65c316dcbdf912d3518c37
+  checksum: 65f235ffa2e7410e454a75d4e1d62fc751e50282b16d7501dc30297b7c980d97c2ef24957c829ff2a0de07b2906d00c37476a90505e7081d8d1a1e0780ef5c0d
   languageName: node
   linkType: hard
 
-"@docusaurus/plugin-sitemap@npm:2.0.0-rc.1":
-  version: 2.0.0-rc.1
-  resolution: "@docusaurus/plugin-sitemap@npm:2.0.0-rc.1"
-  dependencies:
-    "@docusaurus/core": 2.0.0-rc.1
-    "@docusaurus/logger": 2.0.0-rc.1
-    "@docusaurus/types": 2.0.0-rc.1
-    "@docusaurus/utils": 2.0.0-rc.1
-    "@docusaurus/utils-common": 2.0.0-rc.1
-    "@docusaurus/utils-validation": 2.0.0-rc.1
+"@docusaurus/plugin-sitemap@npm:2.0.1":
+  version: 2.0.1
+  resolution: "@docusaurus/plugin-sitemap@npm:2.0.1"
+  dependencies:
+    "@docusaurus/core": 2.0.1
+    "@docusaurus/logger": 2.0.1
+    "@docusaurus/types": 2.0.1
+    "@docusaurus/utils": 2.0.1
+    "@docusaurus/utils-common": 2.0.1
+    "@docusaurus/utils-validation": 2.0.1
     fs-extra: ^10.1.0
     sitemap: ^7.1.1
     tslib: ^2.4.0
   peerDependencies:
     react: ^16.8.4 || ^17.0.0
     react-dom: ^16.8.4 || ^17.0.0
-  checksum: 9e559356e3c4c117f1464f4f4fc0f53a7c4b25adc0c0d12e01daf2be5467326b9cd13879ecd10bac11e662ce1dd0a61c2043d2686abd47e1e2d1caa5b495c3de
+  checksum: 0a4e8658805a14e47d8732d9974e77060f56746f541fdd7a7683b15f4e1e737729db4c73812f68fc1f0fb3eb5d9453b654729ff41e9bca7834ca3fa76518a20e
   languageName: node
   linkType: hard
 
-"@docusaurus/preset-classic@npm:2.0.0-rc.1":
-  version: 2.0.0-rc.1
-  resolution: "@docusaurus/preset-classic@npm:2.0.0-rc.1"
-  dependencies:
-    "@docusaurus/core": 2.0.0-rc.1
-    "@docusaurus/plugin-content-blog": 2.0.0-rc.1
-    "@docusaurus/plugin-content-docs": 2.0.0-rc.1
-    "@docusaurus/plugin-content-pages": 2.0.0-rc.1
-    "@docusaurus/plugin-debug": 2.0.0-rc.1
-    "@docusaurus/plugin-google-analytics": 2.0.0-rc.1
-    "@docusaurus/plugin-google-gtag": 2.0.0-rc.1
-    "@docusaurus/plugin-sitemap": 2.0.0-rc.1
-    "@docusaurus/theme-classic": 2.0.0-rc.1
-    "@docusaurus/theme-common": 2.0.0-rc.1
-    "@docusaurus/theme-search-algolia": 2.0.0-rc.1
-    "@docusaurus/types": 2.0.0-rc.1
+"@docusaurus/preset-classic@npm:2.0.1":
+  version: 2.0.1
+  resolution: "@docusaurus/preset-classic@npm:2.0.1"
+  dependencies:
+    "@docusaurus/core": 2.0.1
+    "@docusaurus/plugin-content-blog": 2.0.1
+    "@docusaurus/plugin-content-docs": 2.0.1
+    "@docusaurus/plugin-content-pages": 2.0.1
+    "@docusaurus/plugin-debug": 2.0.1
+    "@docusaurus/plugin-google-analytics": 2.0.1
+    "@docusaurus/plugin-google-gtag": 2.0.1
+    "@docusaurus/plugin-sitemap": 2.0.1
+    "@docusaurus/theme-classic": 2.0.1
+    "@docusaurus/theme-common": 2.0.1
+    "@docusaurus/theme-search-algolia": 2.0.1
+    "@docusaurus/types": 2.0.1
   peerDependencies:
     react: ^16.8.4 || ^17.0.0
     react-dom: ^16.8.4 || ^17.0.0
-  checksum: 25a10a1fd27627665d58119c02401cd7c4ac208c6413b40f99827d01d5a697d803bf85b144616e3bd48bd9b10178e5f23e2f053ab6e8719496896eea94cf66d8
+  checksum: a9ad076f5fab211e4c036e1adbcc359a31ba3c8945c75a2a43efc47d5914e21dec94bfec17f438b7505f649f428616b0bda5cf206b61ad3f6c69a2c907d14a8c
   languageName: node
   linkType: hard
 
@@ -2458,22 +2458,22 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@docusaurus/theme-classic@npm:2.0.0-rc.1":
-  version: 2.0.0-rc.1
-  resolution: "@docusaurus/theme-classic@npm:2.0.0-rc.1"
-  dependencies:
-    "@docusaurus/core": 2.0.0-rc.1
-    "@docusaurus/mdx-loader": 2.0.0-rc.1
-    "@docusaurus/module-type-aliases": 2.0.0-rc.1
-    "@docusaurus/plugin-content-blog": 2.0.0-rc.1
-    "@docusaurus/plugin-content-docs": 2.0.0-rc.1
-    "@docusaurus/plugin-content-pages": 2.0.0-rc.1
-    "@docusaurus/theme-common": 2.0.0-rc.1
-    "@docusaurus/theme-translations": 2.0.0-rc.1
-    "@docusaurus/types": 2.0.0-rc.1
-    "@docusaurus/utils": 2.0.0-rc.1
-    "@docusaurus/utils-common": 2.0.0-rc.1
-    "@docusaurus/utils-validation": 2.0.0-rc.1
+"@docusaurus/theme-classic@npm:2.0.1":
+  version: 2.0.1
+  resolution: "@docusaurus/theme-classic@npm:2.0.1"
+  dependencies:
+    "@docusaurus/core": 2.0.1
+    "@docusaurus/mdx-loader": 2.0.1
+    "@docusaurus/module-type-aliases": 2.0.1
+    "@docusaurus/plugin-content-blog": 2.0.1
+    "@docusaurus/plugin-content-docs": 2.0.1
+    "@docusaurus/plugin-content-pages": 2.0.1
+    "@docusaurus/theme-common": 2.0.1
+    "@docusaurus/theme-translations": 2.0.1
+    "@docusaurus/types": 2.0.1
+    "@docusaurus/utils": 2.0.1
+    "@docusaurus/utils-common": 2.0.1
+    "@docusaurus/utils-validation": 2.0.1
     "@mdx-js/react": ^1.6.22
     clsx: ^1.2.1
     copy-text-to-clipboard: ^3.0.1
@@ -2490,20 +2490,20 @@ __metadata:
   peerDependencies:
     react: ^16.8.4 || ^17.0.0
     react-dom: ^16.8.4 || ^17.0.0
-  checksum: 1c74c68f0b1ef7face92086a2c63cede416c346f5b9e3444871c8ad22fbc0fa8400c1ca28aff0203479635a47c690a957862f28a6046e65bbdafc4123fbbbd6c
+  checksum: f37077f394ed16a0cb8d810afb744ca8827a264c057940c42ead8f7590cf43e07e979bc24d8cac3538e218ef853814a65aec0bc5fe03c5643590a9bf2735cbdd
   languageName: node
   linkType: hard
 
-"@docusaurus/theme-common@npm:2.0.0-rc.1":
-  version: 2.0.0-rc.1
-  resolution: "@docusaurus/theme-common@npm:2.0.0-rc.1"
-  dependencies:
-    "@docusaurus/mdx-loader": 2.0.0-rc.1
-    "@docusaurus/module-type-aliases": 2.0.0-rc.1
-    "@docusaurus/plugin-content-blog": 2.0.0-rc.1
-    "@docusaurus/plugin-content-docs": 2.0.0-rc.1
-    "@docusaurus/plugin-content-pages": 2.0.0-rc.1
-    "@docusaurus/utils": 2.0.0-rc.1
+"@docusaurus/theme-common@npm:2.0.1":
+  version: 2.0.1
+  resolution: "@docusaurus/theme-common@npm:2.0.1"
+  dependencies:
+    "@docusaurus/mdx-loader": 2.0.1
+    "@docusaurus/module-type-aliases": 2.0.1
+    "@docusaurus/plugin-content-blog": 2.0.1
+    "@docusaurus/plugin-content-docs": 2.0.1
+    "@docusaurus/plugin-content-pages": 2.0.1
+    "@docusaurus/utils": 2.0.1
     "@types/history": ^4.7.11
     "@types/react": "*"
     "@types/react-router-config": "*"
@@ -2515,22 +2515,22 @@ __metadata:
   peerDependencies:
     react: ^16.8.4 || ^17.0.0
     react-dom: ^16.8.4 || ^17.0.0
-  checksum: 3db7e3574c543d96247af630a92c6afcb54ee05bdd2dc66b0b449352ef0555deaff1a498df34c738d837bf5c44197806927e80dbedf8c7886699b7586416c239
+  checksum: 65261e6f777327baa0801fe76728fddfefdb3718b29ae9b30213286450b0f239563c5b2318e4a88f4f1818b26f41e03dcbf02a5dff0e471c859c816716eb225e
   languageName: node
   linkType: hard
 
-"@docusaurus/theme-search-algolia@npm:2.0.0-rc.1":
-  version: 2.0.0-rc.1
-  resolution: "@docusaurus/theme-search-algolia@npm:2.0.0-rc.1"
+"@docusaurus/theme-search-algolia@npm:2.0.1":
+  version: 2.0.1
+  resolution: "@docusaurus/theme-search-algolia@npm:2.0.1"
   dependencies:
     "@docsearch/react": ^3.1.1
-    "@docusaurus/core": 2.0.0-rc.1
-    "@docusaurus/logger": 2.0.0-rc.1
-    "@docusaurus/plugin-content-docs": 2.0.0-rc.1
-    "@docusaurus/theme-common": 2.0.0-rc.1
-    "@docusaurus/theme-translations": 2.0.0-rc.1
-    "@docusaurus/utils": 2.0.0-rc.1
-    "@docusaurus/utils-validation": 2.0.0-rc.1
+    "@docusaurus/core": 2.0.1
+    "@docusaurus/logger": 2.0.1
+    "@docusaurus/plugin-content-docs": 2.0.1
+    "@docusaurus/theme-common": 2.0.1
+    "@docusaurus/theme-translations": 2.0.1
+    "@docusaurus/utils": 2.0.1
+    "@docusaurus/utils-validation": 2.0.1
     algoliasearch: ^4.13.1
     algoliasearch-helper: ^3.10.0
     clsx: ^1.2.1
@@ -2542,23 +2542,23 @@ __metadata:
   peerDependencies:
     react: ^16.8.4 || ^17.0.0
     react-dom: ^16.8.4 || ^17.0.0
-  checksum: 4219cde400494903618d93cfe77c2e9d22dda771fd62da1b0e1e131693d522a372bf10012ffce74efb0f587ffa1b07c267f0b085630fffd832f2e58734519389
+  checksum: 323d6ee93b4cd18aef66f6bdcbae6a1645aec3cbcd7eaddc4ce933a7e3de704fd18934c95d0621ba4655a6cbaf0ef4e738173b09bca3472cf54abd5995b09864
   languageName: node
   linkType: hard
 
-"@docusaurus/theme-translations@npm:2.0.0-rc.1":
-  version: 2.0.0-rc.1
-  resolution: "@docusaurus/theme-translations@npm:2.0.0-rc.1"
+"@docusaurus/theme-translations@npm:2.0.1":
+  version: 2.0.1
+  resolution: "@docusaurus/theme-translations@npm:2.0.1"
   dependencies:
     fs-extra: ^10.1.0
     tslib: ^2.4.0
-  checksum: f2dffe2707b50b3d594a52a942b2653d2d09c25e32e783dcb50f3c822be15984257365aede3924398df9d92c204aa77507852777a81dccba2f12c41a5e3aa696
+  checksum: 0be1398c24e0d866efb3d86f5cf6710453d23e1a31979012a10df9fd027c40452c8468498c246c3f5a34c59bfce40ba5887693e1522ba595a555533870464b96
   languageName: node
   linkType: hard
 
-"@docusaurus/types@npm:2.0.0-rc.1":
-  version: 2.0.0-rc.1
-  resolution: "@docusaurus/types@npm:2.0.0-rc.1"
+"@docusaurus/types@npm:2.0.1":
+  version: 2.0.1
+  resolution: "@docusaurus/types@npm:2.0.1"
   dependencies:
     "@types/history": ^4.7.11
     "@types/react": "*"
@@ -2571,13 +2571,13 @@ __metadata:
   peerDependencies:
     react: ^16.8.4 || ^17.0.0
     react-dom: ^16.8.4 || ^17.0.0
-  checksum: cde2d571890a01b64ba0602375c41cc5f4be93a7e2976e09ccf77b79b3ffd0943412dc0be7987876f39f501d2b395388025b6f11f127502a9f88d648be611a71
+  checksum: bb72dbdba5e5a39d9ebdbfe449f52cd011be62eb1f47176632d13b0800626441551f48b018e239a6ce3d63e474598e6c6f7730aa235fc0b0c653be546cc28875
   languageName: node
   linkType: hard
 
-"@docusaurus/utils-common@npm:2.0.0-rc.1":
-  version: 2.0.0-rc.1
-  resolution: "@docusaurus/utils-common@npm:2.0.0-rc.1"
+"@docusaurus/utils-common@npm:2.0.1":
+  version: 2.0.1
+  resolution: "@docusaurus/utils-common@npm:2.0.1"
   dependencies:
     tslib: ^2.4.0
   peerDependencies:
@@ -2585,28 +2585,28 @@ __metadata:
   peerDependenciesMeta:
     "@docusaurus/types":
       optional: true
-  checksum: c8e86a36d88e2387aa7ac41886b2a67fb0b2be5044476cecc5c3fbab500b1faee55843575807953cd98d385081e92dd42676efc66d3dd1ec9a478fac15de4d5c
+  checksum: f587deb40e3e984643435ee6573b7cbf59e61e7199e5c178b7c1ac01e1c42fa6410d1a3f1783f20067c2f4a6d5c0279dcb401918a948eb4370b9972ff2928691
   languageName: node
   linkType: hard
 
-"@docusaurus/utils-validation@npm:2.0.0-rc.1":
-  version: 2.0.0-rc.1
-  resolution: "@docusaurus/utils-validation@npm:2.0.0-rc.1"
+"@docusaurus/utils-validation@npm:2.0.1":
+  version: 2.0.1
+  resolution: "@docusaurus/utils-validation@npm:2.0.1"
   dependencies:
-    "@docusaurus/logger": 2.0.0-rc.1
-    "@docusaurus/utils": 2.0.0-rc.1
+    "@docusaurus/logger": 2.0.1
+    "@docusaurus/utils": 2.0.1
     joi: ^17.6.0
     js-yaml: ^4.1.0
     tslib: ^2.4.0
-  checksum: d3cb5ffe1d74a524980fc812c8f46dec182daaa4427e8552a0f60cba25cc09b3350687363bc8b163dd65cb0bb9d03440c8740288755dba61ce168e809a4beb27
+  checksum: f856760cd738c0561071a29dd3a67602b4872c2763ddf1a80b9097193f9e9324b466edcad47cf5490cc22e722fc4ccb53d45d510612036312d99877b1cf2dd8e
   languageName: node
   linkType: hard
 
-"@docusaurus/utils@npm:2.0.0-rc.1":
-  version: 2.0.0-rc.1
-  resolution: "@docusaurus/utils@npm:2.0.0-rc.1"
+"@docusaurus/utils@npm:2.0.1":
+  version: 2.0.1
+  resolution: "@docusaurus/utils@npm:2.0.1"
   dependencies:
-    "@docusaurus/logger": 2.0.0-rc.1
+    "@docusaurus/logger": 2.0.1
     "@svgr/webpack": ^6.2.1
     file-loader: ^6.2.0
     fs-extra: ^10.1.0
@@ -2626,7 +2626,7 @@ __metadata:
   peerDependenciesMeta:
     "@docusaurus/types":
       optional: true
-  checksum: adf925d81334d6f055075e1cb4d0a847e30e1f56f819a3674ef625f58919400ac25a18451c5e788d99db20bcc885d671a9a444342a615a816a0522db32f02d46
+  checksum: a0943b23a0a31554606c7e21c709241e3413c44e5b84e9c3d020af886b78942144cb6c360b1c5fe89b140f3e71a5b14a75276e1d3b911411af036a7a36481718
   languageName: node
   linkType: hard
 
@@ -3451,6 +3451,104 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@next/env@npm:12.2.3":
+  version: 12.2.3
+  resolution: "@next/env@npm:12.2.3"
+  checksum: e96dcbea3fbb3d6b6a0799fe2e41c4929ded383659709f5bd00b6479b0711b89891b72fb28753b428933871d5fd91792fa54bc64a74a038c704b5862d40821ec
+  languageName: node
+  linkType: hard
+
+"@next/swc-android-arm-eabi@npm:12.2.3":
+  version: 12.2.3
+  resolution: "@next/swc-android-arm-eabi@npm:12.2.3"
+  conditions: os=android & cpu=arm
+  languageName: node
+  linkType: hard
+
+"@next/swc-android-arm64@npm:12.2.3":
+  version: 12.2.3
+  resolution: "@next/swc-android-arm64@npm:12.2.3"
+  conditions: os=android & cpu=arm64
+  languageName: node
+  linkType: hard
+
+"@next/swc-darwin-arm64@npm:12.2.3":
+  version: 12.2.3
+  resolution: "@next/swc-darwin-arm64@npm:12.2.3"
+  conditions: os=darwin & cpu=arm64
+  languageName: node
+  linkType: hard
+
+"@next/swc-darwin-x64@npm:12.2.3":
+  version: 12.2.3
+  resolution: "@next/swc-darwin-x64@npm:12.2.3"
+  conditions: os=darwin & cpu=x64
+  languageName: node
+  linkType: hard
+
+"@next/swc-freebsd-x64@npm:12.2.3":
+  version: 12.2.3
+  resolution: "@next/swc-freebsd-x64@npm:12.2.3"
+  conditions: os=freebsd & cpu=x64
+  languageName: node
+  linkType: hard
+
+"@next/swc-linux-arm-gnueabihf@npm:12.2.3":
+  version: 12.2.3
+  resolution: "@next/swc-linux-arm-gnueabihf@npm:12.2.3"
+  conditions: os=linux & cpu=arm
+  languageName: node
+  linkType: hard
+
+"@next/swc-linux-arm64-gnu@npm:12.2.3":
+  version: 12.2.3
+  resolution: "@next/swc-linux-arm64-gnu@npm:12.2.3"
+  conditions: os=linux & cpu=arm64 & libc=glibc
+  languageName: node
+  linkType: hard
+
+"@next/swc-linux-arm64-musl@npm:12.2.3":
+  version: 12.2.3
+  resolution: "@next/swc-linux-arm64-musl@npm:12.2.3"
+  conditions: os=linux & cpu=arm64 & libc=musl
+  languageName: node
+  linkType: hard
+
+"@next/swc-linux-x64-gnu@npm:12.2.3":
+  version: 12.2.3
+  resolution: "@next/swc-linux-x64-gnu@npm:12.2.3"
+  conditions: os=linux & cpu=x64 & libc=glibc
+  languageName: node
+  linkType: hard
+
+"@next/swc-linux-x64-musl@npm:12.2.3":
+  version: 12.2.3
+  resolution: "@next/swc-linux-x64-musl@npm:12.2.3"
+  conditions: os=linux & cpu=x64 & libc=musl
+  languageName: node
+  linkType: hard
+
+"@next/swc-win32-arm64-msvc@npm:12.2.3":
+  version: 12.2.3
+  resolution: "@next/swc-win32-arm64-msvc@npm:12.2.3"
+  conditions: os=win32 & cpu=arm64
+  languageName: node
+  linkType: hard
+
+"@next/swc-win32-ia32-msvc@npm:12.2.3":
+  version: 12.2.3
+  resolution: "@next/swc-win32-ia32-msvc@npm:12.2.3"
+  conditions: os=win32 & cpu=ia32
+  languageName: node
+  linkType: hard
+
+"@next/swc-win32-x64-msvc@npm:12.2.3":
+  version: 12.2.3
+  resolution: "@next/swc-win32-x64-msvc@npm:12.2.3"
+  conditions: os=win32 & cpu=x64
+  languageName: node
+  linkType: hard
+
 "@nodelib/fs.scandir@npm:2.1.5":
   version: 2.1.5
   resolution: "@nodelib/fs.scandir@npm:2.1.5"
@@ -5770,6 +5868,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@swc/helpers@npm:0.4.3":
+  version: 0.4.3
+  resolution: "@swc/helpers@npm:0.4.3"
+  dependencies:
+    tslib: ^2.4.0
+  checksum: 5c2f173e950dd3929d84ae48b3586a274d5a874e7cf2013b3d8081e4f8c723fa3a4d4e63b263e84bb7f06431f87b640e91a12655410463c81a3dc2bbc15eceda
+  languageName: node
+  linkType: hard
+
 "@szmarczak/http-timer@npm:^1.1.2":
   version: 1.1.2
   resolution: "@szmarczak/http-timer@npm:1.1.2"
@@ -5978,6 +6085,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@types/chrome@npm:^0.0.114":
+  version: 0.0.114
+  resolution: "@types/chrome@npm:0.0.114"
+  dependencies:
+    "@types/filesystem": "*"
+    "@types/har-format": "*"
+  checksum: 7b7c7d7ae72e54011328709b23f6bb2b15e8626af01e7577deae34fd1a0ec52f3f911f9eb9eeb49110a6816ee993450119eb889a286e077c5ce194878415d63f
+  languageName: node
+  linkType: hard
+
 "@types/color-convert@npm:^2.0.0":
   version: 2.0.0
   resolution: "@types/color-convert@npm:2.0.0"
@@ -8794,17 +8911,17 @@ __metadata:
   languageName: node
   linkType: hard
 
-"browserslist@npm:^4.0.0, browserslist@npm:^4.12.0, browserslist@npm:^4.14.5, browserslist@npm:^4.16.6, browserslist@npm:^4.18.1, browserslist@npm:^4.20.2, browserslist@npm:^4.20.3, browserslist@npm:^4.21.2":
-  version: 4.21.2
-  resolution: "browserslist@npm:4.21.2"
+"browserslist@npm:^4.0.0, browserslist@npm:^4.12.0, browserslist@npm:^4.14.5, browserslist@npm:^4.16.6, browserslist@npm:^4.18.1, browserslist@npm:^4.19.1, browserslist@npm:^4.20.2, browserslist@npm:^4.20.3, browserslist@npm:^4.21.2":
+  version: 4.21.3
+  resolution: "browserslist@npm:4.21.3"
   dependencies:
-    caniuse-lite: ^1.0.30001366
-    electron-to-chromium: ^1.4.188
+    caniuse-lite: ^1.0.30001370
+    electron-to-chromium: ^1.4.202
     node-releases: ^2.0.6
-    update-browserslist-db: ^1.0.4
+    update-browserslist-db: ^1.0.5
   bin:
     browserslist: cli.js
-  checksum: 30fe59f8b065f99665ea63819d29c797660f7975857c290f61f570403abed4d7039ca15b6fd21e39a57b87e1a9262f94676114040766fc0da6ccc11faf9fc377
+  checksum: ff512a7bcca1c530e2854bbdfc7be2791d0fb524097a6340e56e1d5924164c7e4e0a9b070de04cdc4c149d15cb4d4275cb7c626ebbce954278a2823aaad2452a
   languageName: node
   linkType: hard
 
@@ -9091,10 +9208,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001109, caniuse-lite@npm:^1.0.30001251, caniuse-lite@npm:^1.0.30001335, caniuse-lite@npm:^1.0.30001366":
-  version: 1.0.30001367
-  resolution: "caniuse-lite@npm:1.0.30001367"
-  checksum: 9912aed182b8b3a834787424b56a0e71b5ecb5d2cb7235fb022227bc3a81202e8a34bebc5dc9cc504c515b4e052f825c36c2db4b0b880c10e195fe63673edfc6
+"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001109, caniuse-lite@npm:^1.0.30001251, caniuse-lite@npm:^1.0.30001332, caniuse-lite@npm:^1.0.30001335, caniuse-lite@npm:^1.0.30001370":
+  version: 1.0.30001373
+  resolution: "caniuse-lite@npm:1.0.30001373"
+  checksum: cd2f027e2fcf66ed3b0e3eccec89df871f951f2e7600944fae2c3f6f1c37ac82392e573c279e15bf851b75f9696472e38d33fd52d964819ffb8af7af4078ceba
   languageName: node
   linkType: hard
 
@@ -11229,10 +11346,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"electron-to-chromium@npm:^1.4.188":
-  version: 1.4.192
-  resolution: "electron-to-chromium@npm:1.4.192"
-  checksum: b4c59ecdfa3c19a3d350a28129bc4162a9707378f69dbf00ea87d138e86696196e4ffcb1b23ba979368117f83333ed1bb26524749994134a06ec6ac9b9800847
+"electron-to-chromium@npm:^1.4.202":
+  version: 1.4.208
+  resolution: "electron-to-chromium@npm:1.4.208"
+  checksum: 522475d950f2bc2610c590112f4202fb776cf2af7f0e1af92f7960c05c8c83c36be8514ca7bfce99dcb3baead7369241389b4fd0a070aa7a3e948db5913d1f2e
   languageName: node
   linkType: hard
 
@@ -13641,9 +13758,9 @@ __metadata:
     "@babel/template": ^7.12.13
     "@babel/traverse": ^7.12.13
     "@codesandbox/sandpack-react": 0.13.15
-    "@docusaurus/core": 2.0.0-rc.1
-    "@docusaurus/module-type-aliases": ^2.0.0-rc.1
-    "@docusaurus/preset-classic": 2.0.0-rc.1
+    "@docusaurus/core": 2.0.1
+    "@docusaurus/module-type-aliases": 2.0.1
+    "@docusaurus/preset-classic": 2.0.1
     "@emotion/css": ^11.9.0
     "@emotion/hash": ^0.8.0
     "@linaria/babel-preset": ^3.0.0-beta.22
@@ -13692,6 +13809,7 @@ __metadata:
     babel-plugin-tester: 10.0.0
     beachball: ^2.20.0
     bestzip: 2.2.0
+    browserslist: ^4.19.1
     csstype: ^3.0.10
     d3-scale-chromatic: ^3.0.0
     enhanced-resolve: ^5.8.2
@@ -13707,6 +13825,7 @@ __metadata:
     eslint-plugin-react-hooks: 4.3.0
     highlight.js: 11.4.0
     jest: 27.2.3
+    jest-chrome: ^0.7.2
     js-beautify: ^1.14.0
     loader-utils: ^2.0.0
     log-symbols: 5.1.0
@@ -13714,6 +13833,7 @@ __metadata:
     monosize: 0.0.3
     monosize-storage-upstash: 0.0.2
     nano-staged: 0.5.0
+    next: ^12.0.5
     prettier: 2.3.1
     prism-react-renderer: 1.2.1
     react: 17.0.2
@@ -13722,6 +13842,7 @@ __metadata:
     rtl-css-js: ^1.16.0
     schema-utils: ^3.1.1
     simple-git-hooks: 2.7.0
+    source-map-js: 1.0.2
     stylis: ^4.0.13
     syncpack: 6.2.0
     tmp: 0.2.1
@@ -15499,6 +15620,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"jest-chrome@npm:^0.7.2":
+  version: 0.7.2
+  resolution: "jest-chrome@npm:0.7.2"
+  dependencies:
+    "@types/chrome": ^0.0.114
+  peerDependencies:
+    jest: ^26.0.1 || ^27.0.0
+  checksum: 1a7ebbe6981a7800504ab016665fc6c2ffd713b528a8d8b61664dd20406977b0b3212805cd0964ca96b870d687822c7965acf71f64991da12fc23a1733585408
+  languageName: node
+  linkType: hard
+
 "jest-circus@npm:^27.2.2, jest-circus@npm:^27.4.6":
   version: 27.4.6
   resolution: "jest-circus@npm:27.4.6"
@@ -17726,6 +17858,75 @@ __metadata:
   languageName: node
   linkType: hard
 
+"next@npm:^12.0.5":
+  version: 12.2.3
+  resolution: "next@npm:12.2.3"
+  dependencies:
+    "@next/env": 12.2.3
+    "@next/swc-android-arm-eabi": 12.2.3
+    "@next/swc-android-arm64": 12.2.3
+    "@next/swc-darwin-arm64": 12.2.3
+    "@next/swc-darwin-x64": 12.2.3
+    "@next/swc-freebsd-x64": 12.2.3
+    "@next/swc-linux-arm-gnueabihf": 12.2.3
+    "@next/swc-linux-arm64-gnu": 12.2.3
+    "@next/swc-linux-arm64-musl": 12.2.3
+    "@next/swc-linux-x64-gnu": 12.2.3
+    "@next/swc-linux-x64-musl": 12.2.3
+    "@next/swc-win32-arm64-msvc": 12.2.3
+    "@next/swc-win32-ia32-msvc": 12.2.3
+    "@next/swc-win32-x64-msvc": 12.2.3
+    "@swc/helpers": 0.4.3
+    caniuse-lite: ^1.0.30001332
+    postcss: 8.4.14
+    styled-jsx: 5.0.2
+    use-sync-external-store: 1.2.0
+  peerDependencies:
+    fibers: ">= 3.1.0"
+    node-sass: ^6.0.0 || ^7.0.0
+    react: ^17.0.2 || ^18.0.0-0
+    react-dom: ^17.0.2 || ^18.0.0-0
+    sass: ^1.3.0
+  dependenciesMeta:
+    "@next/swc-android-arm-eabi":
+      optional: true
+    "@next/swc-android-arm64":
+      optional: true
+    "@next/swc-darwin-arm64":
+      optional: true
+    "@next/swc-darwin-x64":
+      optional: true
+    "@next/swc-freebsd-x64":
+      optional: true
+    "@next/swc-linux-arm-gnueabihf":
+      optional: true
+    "@next/swc-linux-arm64-gnu":
+      optional: true
+    "@next/swc-linux-arm64-musl":
+      optional: true
+    "@next/swc-linux-x64-gnu":
+      optional: true
+    "@next/swc-linux-x64-musl":
+      optional: true
+    "@next/swc-win32-arm64-msvc":
+      optional: true
+    "@next/swc-win32-ia32-msvc":
+      optional: true
+    "@next/swc-win32-x64-msvc":
+      optional: true
+  peerDependenciesMeta:
+    fibers:
+      optional: true
+    node-sass:
+      optional: true
+    sass:
+      optional: true
+  bin:
+    next: dist/bin/next
+  checksum: b13b42fbb327adca51abeef68aca4b31c82297f07eb3a12d31a3bf2c1aa9ca34cf1ab41bc2b9f4d3162623e70a1ddf230da39fd3ce241b1eea113a4a010a11fd
+  languageName: node
+  linkType: hard
+
 "nice-try@npm:^1.0.4":
   version: 1.0.5
   resolution: "nice-try@npm:1.0.5"
@@ -19497,17 +19698,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"postcss@npm:^7.0.14, postcss@npm:^7.0.26, postcss@npm:^7.0.32, postcss@npm:^7.0.36, postcss@npm:^7.0.5, postcss@npm:^7.0.6":
-  version: 7.0.39
-  resolution: "postcss@npm:7.0.39"
-  dependencies:
-    picocolors: ^0.2.1
-    source-map: ^0.6.1
-  checksum: 4ac793f506c23259189064bdc921260d869a115a82b5e713973c5af8e94fbb5721a5cc3e1e26840500d7e1f1fa42a209747c5b1a151918a9bc11f0d7ed9048e3
-  languageName: node
-  linkType: hard
-
-"postcss@npm:^8.2.15, postcss@npm:^8.3.11, postcss@npm:^8.3.5, postcss@npm:^8.4.13, postcss@npm:^8.4.14, postcss@npm:^8.4.7":
+"postcss@npm:8.4.14, postcss@npm:^8.2.15, postcss@npm:^8.3.11, postcss@npm:^8.3.5, postcss@npm:^8.4.13, postcss@npm:^8.4.14, postcss@npm:^8.4.7":
   version: 8.4.14
   resolution: "postcss@npm:8.4.14"
   dependencies:
@@ -19518,6 +19709,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"postcss@npm:^7.0.14, postcss@npm:^7.0.26, postcss@npm:^7.0.32, postcss@npm:^7.0.36, postcss@npm:^7.0.5, postcss@npm:^7.0.6":
+  version: 7.0.39
+  resolution: "postcss@npm:7.0.39"
+  dependencies:
+    picocolors: ^0.2.1
+    source-map: ^0.6.1
+  checksum: 4ac793f506c23259189064bdc921260d869a115a82b5e713973c5af8e94fbb5721a5cc3e1e26840500d7e1f1fa42a209747c5b1a151918a9bc11f0d7ed9048e3
+  languageName: node
+  linkType: hard
+
 "prelude-ls@npm:^1.2.1":
   version: 1.2.1
   resolution: "prelude-ls@npm:1.2.1"
@@ -21930,7 +22131,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"source-map-js@npm:>=0.6.2 <2.0.0, source-map-js@npm:^1.0.1, source-map-js@npm:^1.0.2":
+"source-map-js@npm:1.0.2, source-map-js@npm:>=0.6.2 <2.0.0, source-map-js@npm:^1.0.1, source-map-js@npm:^1.0.2":
   version: 1.0.2
   resolution: "source-map-js@npm:1.0.2"
   checksum: c049a7fc4deb9a7e9b481ae3d424cc793cb4845daa690bc5a05d428bf41bf231ced49b4cf0c9e77f9d42fdb3d20d6187619fc586605f5eabe995a316da8d377c
@@ -21944,6 +22145,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"source-map-js@patch:source-map-js@npm:1.0.2#.yarn/patches/source-map-js-npm-1.0.2-ee4f9f9b30.patch::locator=griffel-repository%40workspace%3A.":
+  version: 1.0.2
+  resolution: "source-map-js@patch:source-map-js@npm%3A1.0.2#.yarn/patches/source-map-js-npm-1.0.2-ee4f9f9b30.patch::version=1.0.2&hash=7077b7&locator=griffel-repository%40workspace%3A."
+  checksum: 7a447cd639b1994616c263889091d1308463ddccd36a350980ac8a238d50517aef8f49d062d8a978791794b77e1504d4372dc6ffd33d09cf4424431d08220bb8
+  languageName: node
+  linkType: hard
+
 "source-map-loader@npm:^3.0.0":
   version: 3.0.1
   resolution: "source-map-loader@npm:3.0.1"
@@ -22566,6 +22774,20 @@ __metadata:
   languageName: node
   linkType: hard
 
+"styled-jsx@npm:5.0.2":
+  version: 5.0.2
+  resolution: "styled-jsx@npm:5.0.2"
+  peerDependencies:
+    react: ">= 16.8.0 || 17.x.x || ^18.0.0-0"
+  peerDependenciesMeta:
+    "@babel/core":
+      optional: true
+    babel-plugin-macros:
+      optional: true
+  checksum: 86d55819ebeabd283a574d2f44f7d3f8fa6b8c28fa41687ece161bf1e910e04965611618921d8f5cd33dc6dae1033b926a70421ae5ea045440a9861edc3e0d87
+  languageName: node
+  linkType: hard
+
 "stylehacks@npm:^5.1.0":
   version: 5.1.0
   resolution: "stylehacks@npm:5.1.0"
@@ -23761,9 +23983,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"update-browserslist-db@npm:^1.0.4":
-  version: 1.0.4
-  resolution: "update-browserslist-db@npm:1.0.4"
+"update-browserslist-db@npm:^1.0.5":
+  version: 1.0.5
+  resolution: "update-browserslist-db@npm:1.0.5"
   dependencies:
     escalade: ^3.1.1
     picocolors: ^1.0.0
@@ -23771,7 +23993,7 @@ __metadata:
     browserslist: ">= 4.21.0"
   bin:
     browserslist-lint: cli.js
-  checksum: 7c7da28d0fc733b17e01c8fa9385ab909eadce64b8ea644e9603867dc368c2e2a6611af8247e72612b23f9e7cb87ac7c7585a05ff94e1759e9d646cbe9bf49a7
+  checksum: 7e425fe5dbbebdccf72a84ce70ec47fc74dce561d28f47bc2b84a1c2b84179a862c2261b18ab66a5e73e261c7e2ef9e11c6129112989d4d52e8f75a56bb923f8
   languageName: node
   linkType: hard
 
@@ -23908,6 +24130,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"use-sync-external-store@npm:1.2.0":
+  version: 1.2.0
+  resolution: "use-sync-external-store@npm:1.2.0"
+  peerDependencies:
+    react: ^16.8.0 || ^17.0.0 || ^18.0.0
+  checksum: 5c639e0f8da3521d605f59ce5be9e094ca772bd44a4ce7322b055a6f58eeed8dda3c94cabd90c7a41fb6fa852210092008afe48f7038792fd47501f33299116a
+  languageName: node
+  linkType: hard
+
 "use@npm:^3.1.0":
   version: 3.1.1
   resolution: "use@npm:3.1.1"


Test patch:
diff --git a/packages/babel-preset/src/assets/absolutePathToRelative.test.ts b/packages/babel-preset/src/assets/absolutePathToRelative.test.ts
new file mode 100644
index 0000000..e1752e7
--- /dev/null
+++ b/packages/babel-preset/src/assets/absolutePathToRelative.test.ts
@@ -0,0 +1,44 @@
+import * as path from 'path';
+import { absolutePathToRelative } from './absolutePathToRelative';
+
+describe('absolutePathToRelative', () => {
+  it('handles POSIX paths', () => {
+    expect(
+      absolutePathToRelative(
+        path.posix,
+        '/home/projects/foo',
+        '/home/projects/foo/src/styles/Component.styles.ts',
+        'assets/image.png',
+      ),
+    ).toBe('../../assets/image.png');
+
+    expect(
+      absolutePathToRelative(
+        path.posix,
+        '/home/projects/foo',
+        '/home/projects/foo/src/styles/Component.styles.ts',
+        'src/styles/Component.png',
+      ),
+    ).toBe('./Component.png');
+  });
+
+  it('handles Windows paths', () => {
+    expect(
+      absolutePathToRelative(
+        path.win32,
+        'C:\\Users\\Foo\\projects',
+        'C:\\Users\\Foo\\projects\\src\\styles\\Component.styles.ts',
+        'assets/image.png',
+      ),
+    ).toBe('../../assets/image.png');
+
+    expect(
+      absolutePathToRelative(
+        path.win32,
+        'C:\\Users\\Foo\\projects',
+        'C:\\Users\\Foo\\projects\\src\\styles\\Component.styles.ts',
+        'src/styles/Component.png',
+      ),
+    ).toBe('./Component.png');
+  });
+});
diff --git a/packages/babel-preset/src/assets/normalizeStyleRules.test.ts b/packages/babel-preset/src/assets/normalizeStyleRules.test.ts
index c2c5a6a..32829ff 100644
--- a/packages/babel-preset/src/assets/normalizeStyleRules.test.ts
+++ b/packages/babel-preset/src/assets/normalizeStyleRules.test.ts
@@ -1,9 +1,11 @@
+import * as path from 'path';
 import { normalizeStyleRule, normalizeStyleRules } from './normalizeStyleRules';
 
 describe('normalizeStyleRule', () => {
   it('handles rules without quotes', () => {
     expect(
       normalizeStyleRule(
+        path.posix,
         '/home/projects/foo',
         '/home/projects/foo/src/styles/Component.styles.ts',
         'url(../../assets/image.png)',
@@ -14,6 +16,7 @@ describe('normalizeStyleRule', () => {
   it('handles rules with quotes', () => {
     expect(
       normalizeStyleRule(
+        path.posix,
         '/home/projects/foo',
         '/home/projects/foo/src/styles/Component.styles.ts',
         "url('../../assets/image.png')",
@@ -21,6 +24,7 @@ describe('normalizeStyleRule', () => {
     ).toBe('url(assets/image.png)');
     expect(
       normalizeStyleRule(
+        path.posix,
         '/home/projects/foo',
         '/home/projects/foo/src/styles/Component.styles.ts',
         'url("../../assets/image.png")',
@@ -31,18 +35,31 @@ describe('normalizeStyleRule', () => {
   it('keeps data-url', () => {
     expect(
       normalizeStyleRule(
+        path.posix,
         '/home/projects/foo',
         '/home/projects/foo/src/styles/Component.styles.ts',
         'url(data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2Q==)',
       ),
     ).toBe('url(data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2Q==)');
   });
+
+  it('handles Windows paths', () => {
+    expect(
+      normalizeStyleRule(
+        path.win32,
+        'C:\\Users\\Foo\\projects\\bar',
+        'C:\\Users\\Foo\\projects\\bar\\src\\styles\\Component.styles.ts',
+        'url(../../assets/image.png)',
+      ),
+    ).toBe('url(assets/image.png)');
+  });
 });
 
 describe('normalizeStyleRules', () => {
   it('handles rules without metadata', () => {
     expect(
       normalizeStyleRules(
+        path.posix,
         '/home/projects/foo',
         '/home/projects/foo/src/styles/Component.styles.ts',
 
diff --git a/packages/babel-preset/src/assets/replaceAssetsWithImports.test.ts b/packages/babel-preset/src/assets/replaceAssetsWithImports.test.ts
deleted file mode 100644
index 36ff10f..0000000
--- a/packages/babel-preset/src/assets/replaceAssetsWithImports.test.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { absolutePathToRelative } from './replaceAssetsWithImports';
-
-it('absolutePathToRelative', () => {
-  expect(
-    absolutePathToRelative(
-      '/home/projects/foo',
-      '/home/projects/foo/src/styles/Component.styles.ts',
-      'assets/image.png',
-    ),
-  ).toBe('../../assets/image.png');
-
-  expect(
-    absolutePathToRelative(
-      '/home/projects/foo',
-      '/home/projects/foo/src/styles/Component.styles.ts',
-      'src/styles/Component.png',
-    ),
-  ).toBe('./Component.png');
-});
diff --git a/packages/core/src/shorthands/transition.test.ts b/packages/core/src/shorthands/transition.test.ts
new file mode 100644
index 0000000..e8ec883
--- /dev/null
+++ b/packages/core/src/shorthands/transition.test.ts
@@ -0,0 +1,52 @@
+import { transition } from './transition';
+
+describe('transition', () => {
+  it('for a global value', () => {
+    expect(transition('initial')).toEqual<ReturnType<typeof transition>>({
+      transitionDelay: 'initial',
+      transitionTimingFunction: 'initial',
+      transitionProperty: 'initial',
+      transitionDuration: 'initial',
+    });
+  });
+
+  it('for a property name and duration', () => {
+    expect(transition('margin', '2s')).toEqual<ReturnType<typeof transition>>({
+      transitionProperty: 'margin',
+      transitionDuration: '2s',
+      transitionDelay: '0s',
+      transitionTimingFunction: 'ease',
+    });
+  });
+
+  it('for a given name, duration and delay', () => {
+    expect(transition('margin', '2s', '1s')).toEqual<ReturnType<typeof transition>>({
+      transitionProperty: 'margin',
+      transitionDuration: '2s',
+      transitionDelay: '1s',
+      transitionTimingFunction: 'ease',
+    });
+  });
+
+  it('for a given name, duration, delay and easing-function', () => {
+    expect(transition('margin', '2s', '1s', 'ease-in')).toEqual<ReturnType<typeof transition>>({
+      transitionProperty: 'margin',
+      transitionDuration: '2s',
+      transitionDelay: '1s',
+      transitionTimingFunction: 'ease-in',
+    });
+  });
+  it('for multiple properties', () => {
+    expect(
+      transition([
+        ['margin', '2s', '1s', 'ease-in'],
+        ['padding', '3s', '2s', 'ease-out'],
+      ]),
+    ).toEqual<ReturnType<typeof transition>>({
+      transitionProperty: 'margin, padding',
+      transitionDuration: '2s, 3s',
+      transitionDelay: '1s, 2s',
+      transitionTimingFunction: 'ease-in, ease-out',
+    });
+  });
+});
diff --git a/packages/devtools/src/sourceMap/sourceMapConsumer.test.ts b/packages/devtools/src/sourceMap/sourceMapConsumer.test.ts
new file mode 100644
index 0000000..d70fedb
--- /dev/null
+++ b/packages/devtools/src/sourceMap/sourceMapConsumer.test.ts
@@ -0,0 +1,52 @@
+import type { RawSourceMap } from 'source-map-js';
+import { getFilePath, getOriginalPosition, resources } from './sourceMapConsumer';
+
+describe('getOriginalPosition', () => {
+  it('returns input location when sourceMapJSon is invalid', async () => {
+    const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => ({}));
+
+    const sourceMapJSON = {} as RawSourceMap;
+    const sourceLoc = { source: '/src', line: 1, column: 10 };
+    expect(await getOriginalPosition(sourceMapJSON, sourceLoc)).toBe(sourceLoc);
+
+    expect(consoleSpy).toHaveBeenCalledTimes(2);
+    consoleSpy.mockClear();
+  });
+
+  it('returns source url presented in chrome resources', async () => {
+    const sourceMapJSON: RawSourceMap = {
+      version: '3',
+      file: './packages/test1/src/App.styles.js.js',
+      mappings: ';;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA',
+      sources: ['webpack://@testscope/root/./packages/test1/src/App.styles.js?42f2'],
+      sourcesContent: [
+        "import { makeStyles } from '@griffel/react';\\n\\nexport const useStyles = makeStyles({\\n  root: {\\n    display: 'flex',\\n  },\\n});\\n",
+      ],
+      names: [],
+      sourceRoot: '',
+    };
+    const sourceLoc = { source: 'webpack-internal:///./packages/test1/src/App.styles.js', line: 8, column: 77 };
+
+    expect(await getOriginalPosition(sourceMapJSON, sourceLoc)).toEqual({
+      column: 0,
+      line: 3,
+      name: null,
+      // resources is mocked in jest.setup.js
+      source: (await resources)[0].url,
+    });
+  });
+});
+
+describe('getFilePath', () => {
+  it('returns url path', () => {
+    expect(getFilePath('webpack-interal://src/a')).toBe('src/a');
+    expect(getFilePath('webpack://src/a')).toBe('src/a');
+    expect(getFilePath('/src/a')).toBe('src/a');
+  });
+
+  it('returns url path without @ prefix', () => {
+    // Webpack uses name in package.json in source map url, which can have '@' prefix.
+    // But the url in chrome resources always omit the '@' prefix
+    expect(getFilePath('webpack://@test/pkg/src/a')).toBe('test/pkg/src/a');
+  });
+});
diff --git a/packages/webpack-extraction-plugin/src/GriffelCSSExtractionPlugin.test.ts b/packages/webpack-extraction-plugin/src/GriffelCSSExtractionPlugin.test.ts
index 0d755f4..102ea5c 100644
--- a/packages/webpack-extraction-plugin/src/GriffelCSSExtractionPlugin.test.ts
+++ b/packages/webpack-extraction-plugin/src/GriffelCSSExtractionPlugin.test.ts
@@ -36,6 +36,9 @@ async function compileSourceWithWebpack(
       pathinfo: false,
       assetModuleFilename: '[name][ext]',
     },
+    externals: {
+      '@griffel/react': 'Griffel',
+    },
 
     module: {
       rules: [
@@ -64,10 +67,6 @@ async function compileSourceWithWebpack(
     ],
 
     resolve: {
-      alias: {
-        '@griffel/core': path.resolve(__dirname, '..', '..', '..', 'dist', 'packages', 'react', 'index.esm.js'),
-        '@griffel/react': path.resolve(__dirname, '..', '..', '..', 'dist', 'packages', 'react', 'index.esm.js'),
-      },
       extensions: ['.js', '.ts'],
     },
   };
diff --git a/packages/webpack-extraction-plugin/src/webpackLoader.test.ts b/packages/webpack-extraction-plugin/src/webpackLoader.test.ts
index 84796ea..e813775 100644
--- a/packages/webpack-extraction-plugin/src/webpackLoader.test.ts
+++ b/packages/webpack-extraction-plugin/src/webpackLoader.test.ts
@@ -25,6 +25,9 @@ async function compileSourceWithWebpack(entryPath: string, options: CompileOptio
       filename: 'bundle.js',
       pathinfo: false,
     },
+    externals: {
+      '@griffel/react': 'Griffel',
+    },
 
     module: {
       rules: [
@@ -43,10 +46,6 @@ async function compileSourceWithWebpack(entryPath: string, options: CompileOptio
     },
 
     resolve: {
-      alias: {
-        '@griffel/core': path.resolve(__dirname, '..', '..', '..', 'dist', 'packages', 'react', 'index.esm.js'),
-        '@griffel/react': path.resolve(__dirname, '..', '..', '..', 'dist', 'packages', 'react', 'index.esm.js'),
-      },
       extensions: ['.js', '.ts'],
     },
   };