Repository: microsoft/griffel
Pull request #88 (2022-03-30 17:31:42)

Browser devtools: generate data
This PR aims to create data for Griffel brower devtool.

👉 TODO before closing PR - verify no changes are introduced in production bundle

## Basic concept of Griffel browser devtool 
Styles are applied to a DOM element through classNames. The classNames are returned by either `makeStyles` or `mergeClasses` call. The merging of classNames can be considered as a tree structure.

Example:
```tsx
const useButtonStyles = makeStyles({
  red: {
    color: 'red',
  },
  green: {
    color: 'green',
  },
  backgroundBlue: {
    backgroundColor: 'blue',
  },
});

const TwoButtons = ({ green, backgroundBlue }) => {
  const classes = useButtonStyles();
  const mergedClasses1 = mergeClasses(classes.red, green && classes.green);
  const mergedClasses2 = mergeClasses(mergedClasses1, backgroundBlue && classes.backgroundBlue);
  return (
    <>
      <button {...props} className={mergedClasses1} />
      <button {...props} id="blue-button" className={mergedClasses2} />
    </>
  );
};
```
Styles for button with id 'blue-button' are constructed like so:
<img width="826" alt="Screenshot 2022-03-30 at 18 37 55" src="https://user-images.githubusercontent.com/28751745/160887196-bc074f46-f2c3-4c0b-83c3-4920b4cd197d.png">

This PR will create the above tree structure, and devtool can rely on it for the UI.

## How the data (tree) is created:

### Step1, update sequence hash

#### Reason:
In Griffel, a sequence hash prefixed with `__` is used to represent a set of atomic styles. A sequence hash is also applied to DOM element's className. Devtool can rely on this sequence hash in DOM className to resolve the styles. 

Sequence hash is computed based on atomic styles only, without information of how styles are merged. Therefore it is not enough to construct the tree. Consider the above example: 

When `useButtonStyles` is called, hashes will be computed for all styles. 
`color: green` will have a sequence hash like __xxxxxxx. `color: red` will have its own hash as well.

When `mergeClasses1` is calculated, the 2 styles being merged are `color: red` and `color: green`; 
Griffel will compute sequence hash of the merged result: `color: green`, which is the same as the previously computed hash in `useButtonStyles` call. 

Given the sequence hash of `color: green`, there's no way to know if it is computed in `makeStyles` call or `mergeClasses` call.

#### When devtool is enabled,
A suffix is added to sequence hash, representing the order of classNames merging.

When `useButtonStyles` is called, `color: green` will have a sequence hash like __xxxxxxx_0000000.
When `color: red` and `color: green` are merged, the generated `color: green` will be represented by a new sequence hash: __xxxxxxx_yyyyyyy, where yyyyy is a hash of all merged sequences (in this case, sequence hash of `color: red`, and sequence hash of `color: green`).

Given an updated sequence hash __xxxxxxx_yyyyyyy, it is possible to create the tree structure representing styles merging.

### Step2, build a tree

Given a DOM element's className, we can compute a tree structure.
The leaf nodes are styles in `makeStyles` calls. The parent nodes are `mergeClasses`.
Leaf nodes contains also atomic css rules and its related className.

Generated data for the above example on button with id 'blue-button':
<img width="711" alt="Screenshot 2022-04-09 at 17 24 20" src="https://user-images.githubusercontent.com/28751745/162580799-3c29395c-0957-4bd0-a3fe-fe73e7ac6def.png">


YuanboXue-Amber: CI is failing because of failed unit tests due to sequence hash update:
<img width="404" alt="image" src="https://user-images.githubusercontent.com/28751745/161022965-25044f4c-1a93-4b34-b2e9-685319c6fd1c.png">

Do we want to run test against with/without debug sequence?

Patch:
diff --git a/.github/workflows/bundle-size.yml b/.github/workflows/bundle-size.yml
new file mode 100644
index 0000000..61f2735
--- /dev/null
+++ b/.github/workflows/bundle-size.yml
@@ -0,0 +1,70 @@
+name: Bundle size
+on:
+  push:
+    branches:
+      - main
+  pull_request:
+
+jobs:
+  bundle-size:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          fetch-depth: 0
+
+      - name: Derive appropriate SHAs for base and head for `nx affected` commands
+        uses: nrwl/nx-set-shas@v2
+
+      - uses: actions/setup-node@v1
+        with:
+          node-version: '14'
+
+      - name: Get Yarn cache directory path
+        id: yarn-cache-dir-path
+        run: echo "::set-output name=dir::$(yarn config get cacheFolder)"
+
+      - name: Restore Yarn cache
+        uses: actions/cache@v2
+        id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
+        with:
+          path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
+          key: yarn-cache-folder-${{ hashFiles('**/yarn.lock', '.yarnrc.yml') }}
+          restore-keys: |
+            yarn-cache-folder-
+
+      - run: yarn install --immutable
+
+      # Steps for PRs
+      - name: Measure affected packages
+        if: ${{ github.event_name == 'pull_request' }}
+        run: yarn nx affected --target=bundle-size --parallel --max-parallel=2
+
+      - name: Compare results with "main" branch
+        if: ${{ github.event_name == 'pull_request' }}
+        run: |
+          yarn monosize compare-reports --branch main --output markdown
+          cp node_modules/monosize/dist/monosize.md monosize.md
+
+      - name: Save PR number
+        run: echo ${{ github.event.number }} > pr.txt
+
+      - uses: actions/upload-artifact@v3
+        if: ${{ github.event_name == 'pull_request' }}
+        with:
+          name: monosize-report
+          if-no-files-found: error
+          path: |
+            monosize.md
+            pr.txt
+
+      # Steps for "main" branch
+      - name: Measure all packages
+        if: ${{ github.event_name != 'pull_request' }}
+        run: yarn nx run-many --all --target=bundle-size --parallel --max-parallel=2
+
+      - name: Upload results
+        if: ${{ github.event_name != 'pull_request' }}
+        run: yarn monosize upload-report --branch main --commit-sha $GITHUB_SHA
+        env:
+          UPSTASH_WRITE_TOKEN: ${{ secrets.UPSTASH_WRITE_TOKEN }}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3fa08ad..9994bac 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -35,7 +35,7 @@ jobs:
           restore-keys: |
             yarn-cache-folder-
 
-      - run: yarn install --frozen-lockfile
+      - run: yarn install --immutable
       - run: yarn check-dependencies
       - run: yarn nx affected --target=lint --parallel --max-parallel=3
       - run: yarn nx affected --target=type-check --parallel --max-parallel=3
@@ -70,7 +70,7 @@ jobs:
           restore-keys: |
             yarn-cache-folder-
 
-      - run: yarn install --frozen-lockfile
+      - run: yarn install --immutable
       - run: yarn check-dependencies
       - run: yarn nx affected --target=lint --parallel --max-parallel=3
       - run: yarn nx affected --target=type-check --parallel --max-parallel=3
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 95c2c88..d0caaf1 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -36,7 +36,7 @@ jobs:
           restore-keys: |
             yarn-cache-folder-
 
-      - run: yarn install --frozen-lockfile
+      - run: yarn install --immutable
       - run: yarn nx run-many --target=build --projects=@griffel/website --parallel --max-parallel=3
 
       - name: Deploy
diff --git a/.yarn/patches/monosize-npm-0.0.3-9b650c3f51 b/.yarn/patches/monosize-npm-0.0.3-9b650c3f51
new file mode 100644
index 0000000..42d4585
--- /dev/null
+++ b/.yarn/patches/monosize-npm-0.0.3-9b650c3f51
@@ -0,0 +1,13 @@
+diff --git a/src/commands/uploadReport.js b/src/commands/uploadReport.js
+index 99b501ec71b2d491ff69f88efe49b14d0e495ac1..3e5b333bbb501c20f43c821870979cedd67f8e51 100644
+--- a/src/commands/uploadReport.js
++++ b/src/commands/uploadReport.js
+@@ -25,7 +25,7 @@ function uploadReport(options) {
+             yield config.storage.uploadReportToRemote(branch, commitSHA, localReport);
+         }
+         catch (e) {
+-            console.log( /* TODO: proper reporting */);
++            console.log(e /* TODO: proper reporting */);
+             process.exit(1);
+         }
+         if (!quiet) {
diff --git a/apps/website/docs/react/api/make-styles.md b/apps/website/docs/react/api/make-styles.md
index 4466373..5577977 100644
--- a/apps/website/docs/react/api/make-styles.md
+++ b/apps/website/docs/react/api/make-styles.md
@@ -213,3 +213,29 @@ const useClasses = makeStyles({
   },
 });
 ```
+
+### CSS Fallback Properties
+
+Griffel supports CSS fallback properties in order to support older browsers.
+
+Any CSS property accepts an array of values which are all added to the styles.
+Every browser will use the latest valid value (which might be a different one in different browsers, based on supported CSS in that browser): 
+
+```js
+import { makeStyles } from '@griffel/react';
+
+const useClasses = makeStyles({
+  root: {
+    overflowY: ['scroll', 'overlay'],
+  },
+});
+```
+
+<OutputTitle>Produces following CSS...</OutputTitle>
+
+```css
+.f1qdoogn{
+  overflow-y: scroll;  /* Fallback for browsers which do not support overflow: overlay */
+  overflow-y: overlay; /* Used by browsers which support overflow: overlay */
+}
+```
diff --git a/change/@griffel-babel-preset-893a7a76-6351-4d7d-95f0-4f85fc59cb2b.json b/change/@griffel-babel-preset-893a7a76-6351-4d7d-95f0-4f85fc59cb2b.json
deleted file mode 100644
index f81d2ea..0000000
--- a/change/@griffel-babel-preset-893a7a76-6351-4d7d-95f0-4f85fc59cb2b.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "type": "minor",
-  "comment": "feat: export shaker evaluator",
-  "packageName": "@griffel/babel-preset",
-  "email": "olfedias@microsoft.com",
-  "dependentChangeType": "patch"
-}
diff --git a/change/@griffel-babel-preset-99d30eb2-d5e6-4809-8d5f-4bd74bd9b63f.json b/change/@griffel-babel-preset-99d30eb2-d5e6-4809-8d5f-4bd74bd9b63f.json
deleted file mode 100644
index d8c2c6f..0000000
--- a/change/@griffel-babel-preset-99d30eb2-d5e6-4809-8d5f-4bd74bd9b63f.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "type": "none",
-  "comment": "docs: add \"when to use it\" section",
-  "packageName": "@griffel/babel-preset",
-  "email": "olfedias@microsoft.com",
-  "dependentChangeType": "none"
-}
diff --git a/change/@griffel-babel-preset-3ee3f24c-8bd5-4263-9628-7245b5ab02c5.json b/change/@griffel-core-ca8cbc84-e599-46b6-a84c-a0c8427176b0.json
similarity index 51%
rename from change/@griffel-babel-preset-3ee3f24c-8bd5-4263-9628-7245b5ab02c5.json
rename to change/@griffel-core-ca8cbc84-e599-46b6-a84c-a0c8427176b0.json
index b785cda..cb1a47b 100644
--- a/change/@griffel-babel-preset-3ee3f24c-8bd5-4263-9628-7245b5ab02c5.json
+++ b/change/@griffel-core-ca8cbc84-e599-46b6-a84c-a0c8427176b0.json
@@ -1,7 +1,7 @@
 {
   "type": "none",
-  "comment": "update README",
-  "packageName": "@griffel/babel-preset",
+  "comment": "chore: integrate bundle size tooling",
+  "packageName": "@griffel/core",
   "email": "olfedias@microsoft.com",
   "dependentChangeType": "none"
 }
diff --git a/change/@griffel-core-e3d2fa4b-e70f-4d77-b50b-15b28dc47f29.json b/change/@griffel-core-e3d2fa4b-e70f-4d77-b50b-15b28dc47f29.json
deleted file mode 100644
index 5f2a1fe..0000000
--- a/change/@griffel-core-e3d2fa4b-e70f-4d77-b50b-15b28dc47f29.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "type": "patch",
-  "comment": "fix: Improve typings for styles object",
-  "packageName": "@griffel/core",
-  "email": "miroslav.stastny@microsoft.com",
-  "dependentChangeType": "patch"
-}
diff --git a/change/@griffel-core-f0caf7c5-b487-47b5-8dbb-7f23f433c325.json b/change/@griffel-core-f0caf7c5-b487-47b5-8dbb-7f23f433c325.json
deleted file mode 100644
index 15ef622..0000000
--- a/change/@griffel-core-f0caf7c5-b487-47b5-8dbb-7f23f433c325.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "type": "minor",
-  "comment": "feat: add \"styleElementAttributes\" to createDOMRenderer",
-  "packageName": "@griffel/core",
-  "email": "olfedias@microsoft.com",
-  "dependentChangeType": "patch"
-}
diff --git a/change/@griffel-jest-serializer-5907822c-f502-4720-9914-8382838e465a.json b/change/@griffel-jest-serializer-5907822c-f502-4720-9914-8382838e465a.json
deleted file mode 100644
index 4756ccb..0000000
--- a/change/@griffel-jest-serializer-5907822c-f502-4720-9914-8382838e465a.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "type": "none",
-  "comment": "update README, fix comments",
-  "packageName": "@griffel/jest-serializer",
-  "email": "olfedias@microsoft.com",
-  "dependentChangeType": "none"
-}
diff --git a/change/@griffel-react-91a6f79f-816d-4ac0-8d0a-58fa0719a7d3.json b/change/@griffel-react-1c344868-9c9f-4c86-b3fa-32912fd5e641.json
similarity index 70%
rename from change/@griffel-react-91a6f79f-816d-4ac0-8d0a-58fa0719a7d3.json
rename to change/@griffel-react-1c344868-9c9f-4c86-b3fa-32912fd5e641.json
index a699f02..c869fe9 100644
--- a/change/@griffel-react-91a6f79f-816d-4ac0-8d0a-58fa0719a7d3.json
+++ b/change/@griffel-react-1c344868-9c9f-4c86-b3fa-32912fd5e641.json
@@ -1,6 +1,6 @@
 {
   "type": "none",
-  "comment": "docs: add docs for createDOMRenderer",
+  "comment": "chore: integrate bundle size tooling",
   "packageName": "@griffel/react",
   "email": "olfedias@microsoft.com",
   "dependentChangeType": "none"
diff --git a/change/@griffel-react-b23b3a60-675f-4a27-bb5f-c1422001f958.json b/change/@griffel-react-b23b3a60-675f-4a27-bb5f-c1422001f958.json
new file mode 100644
index 0000000..de42bd5
--- /dev/null
+++ b/change/@griffel-react-b23b3a60-675f-4a27-bb5f-c1422001f958.json
@@ -0,0 +1,7 @@
+{
+  "type": "minor",
+  "comment": "feat: generate debug hash sequence for devtools",
+  "packageName": "@griffel/core",
+  "email": "yuanboxue@microsoft.com",
+  "dependentChangeType": "patch"
+}
diff --git a/change/@griffel-webpack-loader-4b1afebf-dac1-470b-a3fa-6b3e926d1c70.json b/change/@griffel-webpack-loader-4b1afebf-dac1-470b-a3fa-6b3e926d1c70.json
deleted file mode 100644
index ef36ab9..0000000
--- a/change/@griffel-webpack-loader-4b1afebf-dac1-470b-a3fa-6b3e926d1c70.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "type": "none",
-  "comment": "update README, fix caller & plugin names",
-  "packageName": "@griffel/webpack-loader",
-  "email": "olfedias@microsoft.com",
-  "dependentChangeType": "none"
-}
diff --git a/change/@griffel-webpack-loader-e2524be4-c006-472d-887b-75edb392d60b.json b/change/@griffel-webpack-loader-e2524be4-c006-472d-887b-75edb392d60b.json
deleted file mode 100644
index 25dc0a4..0000000
--- a/change/@griffel-webpack-loader-e2524be4-c006-472d-887b-75edb392d60b.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "type": "none",
-  "comment": "docs: update README to include configuration guide",
-  "packageName": "@griffel/webpack-loader",
-  "email": "olfedias@microsoft.com",
-  "dependentChangeType": "none"
-}
diff --git a/bundle-size.config.js b/monosize.config.js
similarity index 56%
rename from bundle-size.config.js
rename to monosize.config.js
index d7beaba..a912250 100644
--- a/bundle-size.config.js
+++ b/monosize.config.js
@@ -1,6 +1,12 @@
 const path = require('path');
 
 module.exports = {
+  repository: 'https://github.com/microsoft/griffel',
+  storage: require('monosize-storage-upstash')({
+    url: 'https://us1-nearby-hyena-35747.upstash.io',
+    readonlyToken:
+      'AoujASQgNGQwNGI0YjAtNmExNy00MzNjLWIwNTQtMTIyMGRlODQwZDk2QaCJNVtEfHj6pDLLscOcgMEt-Be1VWcMYbMRR_EqVCw=',
+  }),
   webpack: config => {
     return {
       ...config,
diff --git a/package.json b/package.json
index 53f336d..cc3a6cd 100644
--- a/package.json
+++ b/package.json
@@ -26,7 +26,6 @@
     "@docusaurus/core": "2.0.0-beta.15",
     "@docusaurus/module-type-aliases": "^2.0.0-beta.15",
     "@docusaurus/preset-classic": "2.0.0-beta.15",
-    "@fluentui/bundle-size": "^1.1.4",
     "@nrwl/cli": "13.4.5",
     "@nrwl/eslint-plugin-nx": "13.4.5",
     "@nrwl/jest": "13.4.5",
@@ -74,6 +73,8 @@
     "jest": "27.2.3",
     "js-beautify": "^1.14.0",
     "log-symbols": "5.1.0",
+    "monosize": "0.0.3",
+    "monosize-storage-upstash": "0.0.2",
     "nano-staged": "0.5.0",
     "prettier": "2.3.1",
     "prism-react-renderer": "1.2.1",
@@ -109,6 +110,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",
-    "@docusaurus/theme-common@2.0.0-beta.15": "patch:@docusaurus/theme-common@npm:2.0.0-beta.15#.yarn/patches/@docusaurus-theme-common-npm-2.0.0-beta.15-aaa76f45ef"
+    "@docusaurus/theme-common@2.0.0-beta.15": "patch:@docusaurus/theme-common@npm:2.0.0-beta.15#.yarn/patches/@docusaurus-theme-common-npm-2.0.0-beta.15-aaa76f45ef",
+    "monosize@0.0.3": "patch:monosize@npm:0.0.3#.yarn/patches/monosize-npm-0.0.3-9b650c3f51"
   }
 }
diff --git a/packages/babel-preset/CHANGELOG.json b/packages/babel-preset/CHANGELOG.json
index 1d1f33b..6bc688f 100644
--- a/packages/babel-preset/CHANGELOG.json
+++ b/packages/babel-preset/CHANGELOG.json
@@ -1,6 +1,41 @@
 {
   "name": "@griffel/babel-preset",
   "entries": [
+    {
+      "date": "Wed, 06 Apr 2022 13:28:28 GMT",
+      "tag": "@griffel/babel-preset_v1.2.0",
+      "version": "1.2.0",
+      "comments": {
+        "none": [
+          {
+            "author": "olfedias@microsoft.com",
+            "package": "@griffel/babel-preset",
+            "commit": "cbfd3045dadcce6f2f8348d56eaf882c5e408482",
+            "comment": "update README"
+          },
+          {
+            "author": "olfedias@microsoft.com",
+            "package": "@griffel/babel-preset",
+            "commit": "594f3264239c1c138eeae969713ed09498e95b6c",
+            "comment": "docs: add \"when to use it\" section"
+          }
+        ],
+        "minor": [
+          {
+            "author": "olfedias@microsoft.com",
+            "package": "@griffel/babel-preset",
+            "commit": "699ec126fd85b0b2f8fc924390b5cfa611ffb025",
+            "comment": "feat: export shaker evaluator"
+          },
+          {
+            "author": "beachball",
+            "package": "@griffel/babel-preset",
+            "comment": "Bump @griffel/core to v1.2.0",
+            "commit": "995c356bc77272f6fe69a604db6485d57a9ba7c4"
+          }
+        ]
+      }
+    },
     {
       "date": "Wed, 09 Mar 2022 13:56:06 GMT",
       "tag": "@griffel/babel-preset_v1.1.1",
diff --git a/packages/babel-preset/CHANGELOG.md b/packages/babel-preset/CHANGELOG.md
index dd4b72d..ec3e010 100644
--- a/packages/babel-preset/CHANGELOG.md
+++ b/packages/babel-preset/CHANGELOG.md
@@ -1,9 +1,18 @@
 # Change Log - @griffel/babel-preset
 
-This log was last generated on Wed, 09 Mar 2022 13:56:06 GMT and should not be manually modified.
+This log was last generated on Wed, 06 Apr 2022 13:28:28 GMT and should not be manually modified.
 
 <!-- Start content -->
 
+## 1.2.0
+
+Wed, 06 Apr 2022 13:28:28 GMT
+
+### Minor changes
+
+- feat: export shaker evaluator (olfedias@microsoft.com)
+- Bump @griffel/core to v1.2.0
+
 ## 1.1.1
 
 Wed, 09 Mar 2022 13:56:06 GMT
diff --git a/packages/babel-preset/package.json b/packages/babel-preset/package.json
index 3a4d07a..cf57397 100644
--- a/packages/babel-preset/package.json
+++ b/packages/babel-preset/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@griffel/babel-preset",
-  "version": "1.1.1",
+  "version": "1.2.0",
   "description": "Babel preset with build time transforms for Griffel",
   "license": "MIT",
   "repository": {
@@ -19,6 +19,6 @@
     "tslib": "^2.1.0"
   },
   "peerDependencies": {
-    "@griffel/core": "^1.1.1"
+    "@griffel/core": "^1.2.0"
   }
 }
diff --git a/packages/core/CHANGELOG.json b/packages/core/CHANGELOG.json
index a44e48e..05151e4 100644
--- a/packages/core/CHANGELOG.json
+++ b/packages/core/CHANGELOG.json
@@ -1,6 +1,27 @@
 {
   "name": "@griffel/core",
   "entries": [
+    {
+      "date": "Wed, 06 Apr 2022 13:28:28 GMT",
+      "tag": "@griffel/core_v1.2.0",
+      "version": "1.2.0",
+      "comments": {
+        "minor": [
+          {
+            "author": "miroslav.stastny@microsoft.com",
+            "package": "@griffel/core",
+            "commit": "05635e139c200e609ddb531f40e144b65f9cdcf3",
+            "comment": "feat: CSS fallback values"
+          },
+          {
+            "author": "olfedias@microsoft.com",
+            "package": "@griffel/core",
+            "commit": "2bda4e578a4fcdd1751a3e7496ea9a6df7916012",
+            "comment": "feat: add \"styleElementAttributes\" to createDOMRenderer"
+          }
+        ]
+      }
+    },
     {
       "date": "Wed, 09 Mar 2022 13:56:06 GMT",
       "tag": "@griffel/core_v1.1.1",
diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md
index 5cb4149..227dc95 100644
--- a/packages/core/CHANGELOG.md
+++ b/packages/core/CHANGELOG.md
@@ -1,9 +1,18 @@
 # Change Log - @griffel/core
 
-This log was last generated on Wed, 09 Mar 2022 13:56:06 GMT and should not be manually modified.
+This log was last generated on Wed, 06 Apr 2022 13:28:28 GMT and should not be manually modified.
 
 <!-- Start content -->
 
+## 1.2.0
+
+Wed, 06 Apr 2022 13:28:28 GMT
+
+### Minor changes
+
+- feat: CSS fallback values (miroslav.stastny@microsoft.com)
+- feat: add "styleElementAttributes" to createDOMRenderer (olfedias@microsoft.com)
+
 ## 1.1.1
 
 Wed, 09 Mar 2022 13:56:06 GMT
diff --git a/packages/core/package.json b/packages/core/package.json
index 9a4155a..285fc5a 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@griffel/core",
-  "version": "1.1.1",
+  "version": "1.2.0",
   "description": "DOM implementation of Atomic CSS-in-JS",
   "license": "MIT",
   "repository": {
diff --git a/packages/core/project.json b/packages/core/project.json
index 6d2739a..aeb4b79 100644
--- a/packages/core/project.json
+++ b/packages/core/project.json
@@ -51,7 +51,7 @@
       "dependsOn": [{ "target": "build", "projects": "self" }],
       "options": {
         "cwd": "packages/core",
-        "commands": [{ "command": "bundle-size measure" }]
+        "commands": [{ "command": "monosize measure" }]
       }
     },
     "type-check": {
diff --git a/packages/core/src/constants.ts b/packages/core/src/constants.ts
index d6955d5..2bfd62a 100644
--- a/packages/core/src/constants.ts
+++ b/packages/core/src/constants.ts
@@ -10,6 +10,15 @@ export const SEQUENCE_HASH_LENGTH = 7;
 /** @internal */
 export const SEQUENCE_PREFIX = '___';
 
+/** @internal */
+export const DEBUG_SEQUENCE_SEPARATOR = '_';
+
+/** @internal */
+export const SEQUENCE_SIZE =
+  process.env.NODE_ENV === 'production'
+    ? SEQUENCE_PREFIX.length + SEQUENCE_HASH_LENGTH
+    : SEQUENCE_PREFIX.length + SEQUENCE_HASH_LENGTH + DEBUG_SEQUENCE_SEPARATOR.length + SEQUENCE_HASH_LENGTH;
+
 /** @internal */
 export const DEFINITION_LOOKUP_TABLE: Record<SequenceHash, LookupItem> = {};
 
diff --git a/packages/core/src/devtools/getDebugTree.ts b/packages/core/src/devtools/getDebugTree.ts
new file mode 100644
index 0000000..c83f185
--- /dev/null
+++ b/packages/core/src/devtools/getDebugTree.ts
@@ -0,0 +1,49 @@
+import { DEFINITION_LOOKUP_TABLE } from '../constants';
+import { LookupItem, SequenceHash } from '../types';
+import { debugData } from './store';
+import { DebugSequence } from './types';
+import { getDebugClassNames } from './utils';
+
+export function getDebugTree(debugSequenceHash: SequenceHash, parentNode?: DebugSequence) {
+  const lookupItem: LookupItem | undefined = DEFINITION_LOOKUP_TABLE[debugSequenceHash];
+  if (lookupItem === undefined) {
+    return undefined;
+  }
+
+  const parentLookupItem = parentNode ? DEFINITION_LOOKUP_TABLE[parentNode.sequenceHash] : undefined;
+  const debugClassNames = getDebugClassNames(lookupItem, parentLookupItem, parentNode?.debugClassNames);
+
+  const node: DebugSequence = {
+    sequenceHash: debugSequenceHash,
+    direction: lookupItem[1],
+    children: [],
+    debugClassNames,
+  };
+
+  const childrenSequences = debugData.getChildrenSequences(node.sequenceHash);
+  childrenSequences.forEach((sequence: SequenceHash) => {
+    const child = getDebugTree(sequence, node);
+    if (child) {
+      node.children.push(child);
+    }
+  });
+
+  // if it's leaf (makeStyle node), get css rules
+  if (!node.children.length) {
+    node.rules = {};
+    node.debugClassNames.forEach(({ className }) => {
+      const mapData = debugData.getSequenceDetails(debugSequenceHash);
+      if (mapData) {
+        node.slot = mapData.slotName;
+      }
+
+      const cssRule = debugData.getCSSRules().find(cssRule => {
+        return cssRule.includes(className);
+      });
+
+      node.rules![className] = cssRule!;
+    });
+  }
+
+  return node;
+}
diff --git a/packages/core/src/devtools/index.ts b/packages/core/src/devtools/index.ts
new file mode 100644
index 0000000..4a25916
--- /dev/null
+++ b/packages/core/src/devtools/index.ts
@@ -0,0 +1,5 @@
+export { injectDevTools } from './injectDevTools';
+export { isDevToolsEnabled } from './isDevToolsEnabled';
+export { debugData } from './store';
+
+export type { DebugCSSRules, DebugSequence, DebugResult } from './types';
diff --git a/packages/core/src/devtools/injectDevTools.ts b/packages/core/src/devtools/injectDevTools.ts
new file mode 100644
index 0000000..c4e3f5d
--- /dev/null
+++ b/packages/core/src/devtools/injectDevTools.ts
@@ -0,0 +1,30 @@
+import { SEQUENCE_PREFIX } from '../constants';
+import { getDebugTree } from './getDebugTree';
+
+export function injectDevTools(document: Document) {
+  const window = document.defaultView;
+  if (!window || window.__GRIFFEL_DEVTOOLS__) {
+    return;
+  }
+
+  const devtools: typeof window['__GRIFFEL_DEVTOOLS__'] = {
+    getInfo: (element: HTMLElement) => {
+      const rootDebugSequenceHash = Array.from(element.classList).find(className =>
+        className.startsWith(SEQUENCE_PREFIX),
+      );
+      if (rootDebugSequenceHash === undefined) {
+        return undefined;
+      }
+
+      return getDebugTree(rootDebugSequenceHash);
+    },
+  };
+
+  Object.defineProperty(window, '__GRIFFEL_DEVTOOLS__', {
+    configurable: false,
+    enumerable: false,
+    get() {
+      return devtools;
+    },
+  });
+}
diff --git a/packages/core/src/devtools/isDevToolsEnabled.ts b/packages/core/src/devtools/isDevToolsEnabled.ts
new file mode 100644
index 0000000..44c21d9
--- /dev/null
+++ b/packages/core/src/devtools/isDevToolsEnabled.ts
@@ -0,0 +1,1 @@
+export const isDevToolsEnabled = false;
diff --git a/packages/core/src/devtools/store.ts b/packages/core/src/devtools/store.ts
new file mode 100644
index 0000000..3f629bf
--- /dev/null
+++ b/packages/core/src/devtools/store.ts
@@ -0,0 +1,41 @@
+import { SequenceHash } from '../types';
+import { SEQUENCE_PREFIX, SEQUENCE_SIZE } from '../constants';
+import { mergeClassesCachedResults } from '../mergeClasses';
+
+const sequenceDetails: Record<SequenceHash, { slotName: string }> = {};
+
+const cssRules: string[] = [];
+
+export const debugData = {
+  getChildrenSequences: (debugSequenceHash: SequenceHash): SequenceHash[] => {
+    const key = Object.keys(mergeClassesCachedResults).find(key =>
+      mergeClassesCachedResults[key].startsWith(debugSequenceHash),
+    );
+
+    if (key) {
+      // key of the mergeClasses cache contains merge order
+      return key
+        .split(SEQUENCE_PREFIX)
+        .filter(sequence => sequence.length)
+        .map(sequence => SEQUENCE_PREFIX + sequence);
+    }
+
+    return [];
+  },
+
+  addCSSRule: (rule: string) => {
+    cssRules.push(rule);
+  },
+  addSequenceDetails: <Slots extends string | number>(classNamesForSlots: Record<Slots, string>) => {
+    Object.entries<string>(classNamesForSlots).forEach(([slotName, sequenceHash]) => {
+      sequenceDetails[sequenceHash.substring(0, SEQUENCE_SIZE)] = { slotName };
+    });
+  },
+
+  getCSSRules: (): string[] => {
+    return cssRules;
+  },
+  getSequenceDetails: (sequenceHash: SequenceHash): typeof sequenceDetails[string] | undefined => {
+    return sequenceDetails[sequenceHash];
+  },
+};
diff --git a/packages/core/src/devtools/types.ts b/packages/core/src/devtools/types.ts
new file mode 100644
index 0000000..f9e6a23
--- /dev/null
+++ b/packages/core/src/devtools/types.ts
@@ -0,0 +1,23 @@
+import { SequenceHash } from '../types';
+
+declare global {
+  interface Window {
+    __GRIFFEL_DEVTOOLS__: {
+      getInfo: (el: HTMLElement) => DebugResult | undefined;
+    };
+  }
+}
+
+export type DebugAtomicClassName = { className: string; overriddenBy?: string };
+export type DebugCSSRules = Record<string /* className */, string /* cssRule */>;
+export type DebugSequence = {
+  sequenceHash: SequenceHash;
+  direction: 'ltr' | 'rtl';
+  children: DebugSequence[];
+  debugClassNames: DebugAtomicClassName[];
+
+  slot?: string;
+  rules?: DebugCSSRules;
+};
+
+export type DebugResult = DebugSequence;
diff --git a/packages/core/src/devtools/utils.ts b/packages/core/src/devtools/utils.ts
new file mode 100644
index 0000000..46e798a
--- /dev/null
+++ b/packages/core/src/devtools/utils.ts
@@ -0,0 +1,37 @@
+import { CSSClasses, LookupItem } from '../types';
+import { DebugAtomicClassName } from './types';
+
+function getDirectionalClassName(classes: CSSClasses, direction: 'ltr' | 'rtl'): string {
+  return Array.isArray(classes) ? (direction === 'rtl' ? classes[1] : classes[0]) : classes;
+}
+
+export function getDebugClassNames(
+  lookupItem: LookupItem,
+  parentLookupItem?: LookupItem,
+  parentDebugClassNames?: DebugAtomicClassName[],
+): DebugAtomicClassName[] {
+  const classesMapping = lookupItem[0];
+  const direction = lookupItem[1];
+
+  return Object.entries(classesMapping).map(([propertyHash, classes]) => {
+    const className = getDirectionalClassName(classes, direction);
+
+    let overriddenBy: string | undefined;
+    if (parentDebugClassNames && parentLookupItem) {
+      const matching = parentDebugClassNames.find(({ className: parentClassName }) => parentClassName === className);
+
+      if (!matching && parentLookupItem[0][propertyHash]) {
+        // style is overriden in current merging, by rules in parent debug node
+        overriddenBy = getDirectionalClassName(parentLookupItem[0][propertyHash], parentLookupItem[1]);
+      } else {
+        // overridden status come from higher level of merging
+        overriddenBy = matching?.overriddenBy;
+      }
+    }
+
+    return {
+      className,
+      overriddenBy,
+    };
+  });
+}
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index 0819c08..bd186d1 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -64,3 +64,5 @@ export type {
   MakeStylesOptions,
   GriffelRenderer,
 } from './types';
+
+export type { DebugCSSRules, DebugSequence, DebugResult } from './devtools';
diff --git a/packages/core/src/makeStyles.ts b/packages/core/src/makeStyles.ts
index 078572e..19ce1b9 100644
--- a/packages/core/src/makeStyles.ts
+++ b/packages/core/src/makeStyles.ts
@@ -1,5 +1,6 @@
-import { reduceToClassNameForSlots } from './runtime/reduceToClassNameForSlots';
+import { debugData, isDevToolsEnabled } from './devtools';
 import { resolveStyleRulesForSlots } from './resolveStyleRulesForSlots';
+import { reduceToClassNameForSlots } from './runtime/reduceToClassNameForSlots';
 import { CSSClassesMapBySlot, CSSRulesByBucket, MakeStylesOptions, StylesBySlots } from './types';
 
 export function makeStyles<Slots extends string | number>(stylesBySlots: StylesBySlots<Slots>) {
@@ -36,8 +37,15 @@ export function makeStyles<Slots extends string | number>(stylesBySlots: StylesB
       renderer.insertCSSRules(cssRules!);
       insertionCache[rendererId] = true;
     }
+    const classNamesForSlots = isLTR
+      ? (ltrClassNamesForSlots as Record<Slots, string>)
+      : (rtlClassNamesForSlots as Record<Slots, string>);
+
+    if (process.env.NODE_ENV !== 'production' && isDevToolsEnabled) {
+      debugData.addSequenceDetails(classNamesForSlots!);
+    }
 
-    return isLTR ? (ltrClassNamesForSlots as Record<Slots, string>) : (rtlClassNamesForSlots as Record<Slots, string>);
+    return classNamesForSlots;
   }
 
   return computeClasses;
diff --git a/packages/core/src/mergeClasses.ts b/packages/core/src/mergeClasses.ts
index 37633d3..3cf7a6e 100644
--- a/packages/core/src/mergeClasses.ts
+++ b/packages/core/src/mergeClasses.ts
@@ -2,17 +2,15 @@ import {
   DEFINITION_LOOKUP_TABLE,
   LOOKUP_DEFINITIONS_INDEX,
   LOOKUP_DIR_INDEX,
-  SEQUENCE_HASH_LENGTH,
   SEQUENCE_PREFIX,
+  SEQUENCE_SIZE,
 } from './constants';
 import { hashSequence } from './runtime/utils/hashSequence';
 import { reduceToClassName } from './runtime/reduceToClassNameForSlots';
 import { CSSClassesMap, SequenceHash } from './types';
 
 // Contains a mapping of previously resolved sequences of atomic classnames
-const mergeClassesCachedResults: Record<string, string> = {};
-
-const SEQUENCE_SIZE = SEQUENCE_PREFIX.length + SEQUENCE_HASH_LENGTH;
+export const mergeClassesCachedResults: Record<string, string> = {};
 
 /**
  * Function can take any number of arguments, joins classes together and deduplicates atomic declarations generated by
@@ -139,7 +137,7 @@ export function mergeClasses(): string {
   let atomicClassNames = reduceToClassName(resultDefinitions, dir!);
 
   // Each merge of classes generates a new sequence of atomic classes that needs to be registered
-  const newSequenceHash = hashSequence(atomicClassNames, dir!);
+  const newSequenceHash = hashSequence(atomicClassNames, dir!, sequencesIds);
   atomicClassNames = newSequenceHash + ' ' + atomicClassNames;
 
   mergeClassesCachedResults[sequenceMatch] = atomicClassNames;
diff --git a/packages/core/src/renderer/createDOMRenderer.ts b/packages/core/src/renderer/createDOMRenderer.ts
index ead0828..0f36b4d 100644
--- a/packages/core/src/renderer/createDOMRenderer.ts
+++ b/packages/core/src/renderer/createDOMRenderer.ts
@@ -1,3 +1,4 @@
+import { injectDevTools, isDevToolsEnabled, debugData } from '../devtools';
 import { GriffelRenderer, StyleBucketName } from '../types';
 import { getStyleSheetForBucket } from './getStyleSheetForBucket';
 
@@ -55,6 +56,9 @@ export function createDOMRenderer(
           }
 
           renderer.insertionCache[ruleCSS] = styleBucketName as StyleBucketName;
+          if (process.env.NODE_ENV !== 'production' && isDevToolsEnabled) {
+            debugData.addCSSRule(ruleCSS);
+          }
 
           if (sheet) {
             try {
@@ -78,6 +82,10 @@ export function createDOMRenderer(
     },
   };
 
+  if (target && process.env.NODE_ENV !== 'production' && isDevToolsEnabled) {
+    injectDevTools(target);
+  }
+
   return renderer;
 }
 
diff --git a/packages/core/src/runtime/compileCSS.ts b/packages/core/src/runtime/compileCSS.ts
index 79c73d5..8dfcb5b 100644
--- a/packages/core/src/runtime/compileCSS.ts
+++ b/packages/core/src/runtime/compileCSS.ts
@@ -11,11 +11,11 @@ export interface CompileCSSOptions {
   support: string;
 
   property: string;
-  value: number | string;
+  value: number | string | Array<number | string>;
 
   rtlClassName?: string;
   rtlProperty?: string;
-  rtlValue?: number | string;
+  rtlValue?: number | string | Array<number | string>;
 }
 
 const PSEUDO_SELECTOR_REGEX = /,( *[^ &])/g;
@@ -63,14 +63,18 @@ export function compileCSS(options: CompileCSSOptions): [string /* ltr definitio
   const { className, media, pseudo, support, property, rtlClassName, rtlProperty, rtlValue, value } = options;
 
   const classNameSelector = `.${className}`;
-  const cssDeclaration = `{ ${hyphenateProperty(property)}: ${value}; }`;
+  const cssDeclaration = Array.isArray(value)
+    ? `{ ${value.map(v => `${hyphenateProperty(property)}: ${v}`).join(';')}; }`
+    : `{ ${hyphenateProperty(property)}: ${value}; }`;
 
   let rtlClassNameSelector: string | null = null;
   let rtlCSSDeclaration: string | null = null;
 
   if (rtlProperty && rtlClassName) {
     rtlClassNameSelector = `.${rtlClassName}`;
-    rtlCSSDeclaration = `{ ${hyphenateProperty(rtlProperty)}: ${rtlValue}; }`;
+    rtlCSSDeclaration = Array.isArray(rtlValue)
+      ? `{ ${rtlValue.map(v => `${hyphenateProperty(rtlProperty)}: ${v}`).join(';')}; }`
+      : `{ ${hyphenateProperty(rtlProperty)}: ${rtlValue}; }`;
   }
 
   let cssRule = '';
diff --git a/packages/core/src/runtime/resolveStyleRules.ts b/packages/core/src/runtime/resolveStyleRules.ts
index 3b31482..588c27c 100644
--- a/packages/core/src/runtime/resolveStyleRules.ts
+++ b/packages/core/src/runtime/resolveStyleRules.ts
@@ -171,6 +171,72 @@ export function resolveStyleRules(
         cssRulesByBucket,
         rtlAnimationNames.join(', '),
       );
+    } else if (Array.isArray(value)) {
+      // not animationName property but array in the value => fallback values
+      if (value.length === 0) {
+        if (process.env.NODE_ENV !== 'production') {
+          console.warn(
+            `makeStyles(): An empty array was passed as input to "${property}", the property will be omitted in the styles.`,
+          );
+        }
+        continue;
+      }
+
+      const key = hashPropertyKey(pseudo, media, support, property);
+      const className = hashClassName({
+        media,
+        value: value.map(v => (v ?? '').toString()).join(';'),
+        support,
+        pseudo,
+        property,
+      });
+
+      const rtlDefinitions = value.map(v => convertProperty(property, v!));
+
+      const rtlPropertyConsistent = !rtlDefinitions.some(v => v.key !== rtlDefinitions[0].key);
+
+      if (!rtlPropertyConsistent) {
+        if (process.env.NODE_ENV !== 'production') {
+          console.error(
+            'makeStyles(): mixing CSS fallback values which result in multiple CSS properties in RTL is not supported.',
+          );
+        }
+        continue;
+      }
+
+      const flippedInRtl = rtlDefinitions[0].key !== property || rtlDefinitions.some((v, i) => v.value !== value[i]);
+
+      const rtlClassName = flippedInRtl
+        ? hashClassName({
+            value: rtlDefinitions.map(v => (v?.value ?? '').toString()).join(';'),
+            property: rtlDefinitions[0].key,
+            pseudo,
+            media,
+            support,
+          })
+        : undefined;
+
+      const rtlCompileOptions: Partial<CompileCSSOptions> | undefined = flippedInRtl
+        ? {
+            rtlClassName,
+            rtlProperty: rtlDefinitions[0].key,
+            rtlValue: rtlDefinitions.map(d => d.value) as Array<string | number>,
+          }
+        : undefined;
+
+      const styleBucketName = getStyleBucketName(pseudo, media, support);
+      const [ltrCSS, rtlCSS] = compileCSS({
+        className,
+        media,
+        pseudo,
+        property,
+        support,
+        value: value as Array<string | number>,
+        ...rtlCompileOptions,
+      });
+
+      pushToClassesMap(cssClassesMap, key, className, rtlClassName);
+      pushToCSSRules(cssRulesByBucket, styleBucketName, ltrCSS, rtlCSS);
     } else if (isObject(value)) {
       if (isNestedSelector(property)) {
         resolveStyleRules(
diff --git a/packages/core/src/runtime/utils/hashSequence.ts b/packages/core/src/runtime/utils/hashSequence.ts
index d1b9e82..dc83fa5 100644
--- a/packages/core/src/runtime/utils/hashSequence.ts
+++ b/packages/core/src/runtime/utils/hashSequence.ts
@@ -1,6 +1,6 @@
 import hash from '@emotion/hash';
 
-import { SEQUENCE_HASH_LENGTH, SEQUENCE_PREFIX } from '../../constants';
+import { DEBUG_SEQUENCE_SEPARATOR, SEQUENCE_HASH_LENGTH, SEQUENCE_PREFIX } from '../../constants';
 import { SequenceHash } from '../../types';
 
 function padEndHash(value: string): string {
@@ -17,6 +17,19 @@ function padEndHash(value: string): string {
   return value;
 }
 
-export function hashSequence(classes: string, dir: 'ltr' | 'rtl'): SequenceHash {
-  return SEQUENCE_PREFIX + padEndHash(hash(classes + dir));
+export function hashSequence(
+  classes: string,
+  dir: 'ltr' | 'rtl',
+  sequenceIds: (SequenceHash | undefined)[] = [],
+): SequenceHash {
+  if (process.env.NODE_ENV === 'production') {
+    return SEQUENCE_PREFIX + padEndHash(hash(classes + dir));
+  }
+
+  return (
+    SEQUENCE_PREFIX +
+    padEndHash(hash(classes + dir)) +
+    DEBUG_SEQUENCE_SEPARATOR +
+    padEndHash(hash(sequenceIds.join('')))
+  );
 }
diff --git a/packages/core/src/shorthands/border.ts b/packages/core/src/shorthands/border.ts
index 3c0c455..5294938 100644
--- a/packages/core/src/shorthands/border.ts
+++ b/packages/core/src/shorthands/border.ts
@@ -1,9 +1,8 @@
-import * as CSS from 'csstype';
-
-import type { GriffelStylesStrictCSSObject, GriffelStylesCSSValue } from '../types';
+import type { GriffelStylesStrictCSSObject } from '../types';
 import { borderWidth } from './borderWidth';
 import { borderStyle } from './borderStyle';
 import { borderColor } from './borderColor';
+import { BorderColorInput, BorderStyleInput, BorderWidthInput } from './types';
 
 type BorderStyle = Pick<
   GriffelStylesStrictCSSObject,
@@ -21,16 +20,9 @@ type BorderStyle = Pick<
   | 'borderLeftWidth'
 >;
 
-export function border(width: CSS.Property.BorderWidth<GriffelStylesCSSValue>): BorderStyle;
-export function border(
-  width: CSS.Property.BorderWidth<GriffelStylesCSSValue>,
-  style: CSS.Property.BorderStyle,
-): BorderStyle;
-export function border(
-  width: CSS.Property.BorderWidth<GriffelStylesCSSValue>,
-  style: CSS.Property.BorderStyle,
-  color: CSS.Property.BorderColor,
-): BorderStyle;
+export function border(width: BorderWidthInput): BorderStyle;
+export function border(width: BorderWidthInput, style: BorderStyleInput): BorderStyle;
+export function border(width: BorderWidthInput, style: BorderStyleInput, color: BorderColorInput): BorderStyle;
 
 /**
  * A function that implements expansion for "border" to all sides of an element, it's simplified - check usage examples.
@@ -42,9 +34,7 @@ export function border(
  *
  * See https://developer.mozilla.org/en-US/docs/Web/CSS/border
  */
-export function border(
-  ...values: [CSS.Property.BorderWidth<GriffelStylesCSSValue>, CSS.Property.BorderStyle?, CSS.Property.BorderColor?]
-): BorderStyle {
+export function border(...values: [BorderWidthInput, BorderStyleInput?, BorderColorInput?]): BorderStyle {
   return {
     ...borderWidth(values[0]),
     ...(values[1] && borderStyle(values[1])),
diff --git a/packages/core/src/shorthands/borderBottom.ts b/packages/core/src/shorthands/borderBottom.ts
index bb2be52..49a1d36 100644
--- a/packages/core/src/shorthands/borderBottom.ts
+++ b/packages/core/src/shorthands/borderBottom.ts
@@ -1,20 +1,17 @@
-import * as CSS from 'csstype';
-import type { GriffelStylesStrictCSSObject, GriffelStylesCSSValue } from '../types';
+import type { GriffelStylesStrictCSSObject } from '../types';
+import { BorderColorInput, BorderStyleInput, BorderWidthInput } from './types';
 
 type BorderBottomStyle = Pick<
   GriffelStylesStrictCSSObject,
   'borderBottomWidth' | 'borderBottomStyle' | 'borderBottomColor'
 >;
 
-export function borderBottom(width: CSS.Property.BorderWidth<GriffelStylesCSSValue>): BorderBottomStyle;
+export function borderBottom(width: BorderWidthInput): BorderBottomStyle;
+export function borderBottom(width: BorderWidthInput, style: BorderStyleInput): BorderBottomStyle;
 export function borderBottom(
-  width: CSS.Property.BorderWidth<GriffelStylesCSSValue>,
-  style: CSS.Property.BorderStyle,
-): BorderBottomStyle;
-export function borderBottom(
-  width: CSS.Property.BorderWidth<GriffelStylesCSSValue>,
-  style: CSS.Property.BorderStyle,
-  color: CSS.Property.BorderColor,
+  width: BorderWidthInput,
+  style: BorderStyleInput,
+  color: BorderColorInput,
 ): BorderBottomStyle;
 
 /**
@@ -27,9 +24,7 @@ export function borderBottom(
  *
  * See https://developer.mozilla.org/en-US/docs/Web/CSS/border-bottom
  */
-export function borderBottom(
-  ...values: [CSS.Property.BorderWidth<GriffelStylesCSSValue>, CSS.Property.BorderStyle?, CSS.Property.BorderColor?]
-): BorderBottomStyle {
+export function borderBottom(...values: [BorderWidthInput, BorderStyleInput?, BorderColorInput?]): BorderBottomStyle {
   return {
     borderBottomWidth: values[0],
     ...(values[1] && ({ borderBottomStyle: values[1] } as BorderBottomStyle)),
diff --git a/packages/core/src/shorthands/borderColor.ts b/packages/core/src/shorthands/borderColor.ts
index aef3361..8cc967f 100644
--- a/packages/core/src/shorthands/borderColor.ts
+++ b/packages/core/src/shorthands/borderColor.ts
@@ -1,25 +1,24 @@
-import * as CSS from 'csstype';
-
 import type { GriffelStylesStrictCSSObject } from '../types';
 import { generateStyles } from './generateStyles';
+import { BorderColorInput } from './types';
 
 type BorderColorStyle = Pick<
   GriffelStylesStrictCSSObject,
   'borderTopColor' | 'borderRightColor' | 'borderBottomColor' | 'borderLeftColor'
 >;
 
-export function borderColor(all: CSS.Property.BorderColor): BorderColorStyle;
-export function borderColor(vertical: CSS.Property.BorderColor, horizontal: CSS.Property.BorderColor): BorderColorStyle;
+export function borderColor(all: BorderColorInput): BorderColorStyle;
+export function borderColor(vertical: BorderColorInput, horizontal: BorderColorInput): BorderColorStyle;
 export function borderColor(
-  top: CSS.Property.BorderColor,
-  horizontal: CSS.Property.BorderColor,
-  bottom: CSS.Property.BorderColor,
+  top: BorderColorInput,
+  horizontal: BorderColorInput,
+  bottom: BorderColorInput,
 ): BorderColorStyle;
 export function borderColor(
-  top: CSS.Property.BorderColor,
-  right: CSS.Property.BorderColor,
-  bottom: CSS.Property.BorderColor,
-  left: CSS.Property.BorderColor,
+  top: BorderColorInput,
+  right: BorderColorInput,
+  bottom: BorderColorInput,
+  left: BorderColorInput,
 ): BorderColorStyle;
 
 /**
@@ -33,6 +32,6 @@ export function borderColor(
  *
  * See https://developer.mozilla.org/en-US/docs/Web/CSS/border-color
  */
-export function borderColor(...values: CSS.Property.BorderColor[]): BorderColorStyle {
+export function borderColor(...values: BorderColorInput[]): BorderColorStyle {
   return generateStyles<BorderColorStyle>('border', 'Color', ...values);
 }
diff --git a/packages/core/src/shorthands/borderLeft.ts b/packages/core/src/shorthands/borderLeft.ts
index 1dd25e8..66935ef 100644
--- a/packages/core/src/shorthands/borderLeft.ts
+++ b/packages/core/src/shorthands/borderLeft.ts
@@ -1,18 +1,11 @@
-import * as CSS from 'csstype';
-import type { GriffelStylesStrictCSSObject, GriffelStylesCSSValue } from '../types';
+import type { GriffelStylesStrictCSSObject } from '../types';
+import { BorderColorInput, BorderStyleInput, BorderWidthInput } from './types';
 
 type BorderLeftStyle = Pick<GriffelStylesStrictCSSObject, 'borderLeftColor' | 'borderLeftStyle' | 'borderLeftWidth'>;
 
-export function borderLeft(width: CSS.Property.BorderWidth<GriffelStylesCSSValue>): BorderLeftStyle;
-export function borderLeft(
-  width: CSS.Property.BorderWidth<GriffelStylesCSSValue>,
-  style: CSS.Property.BorderStyle,
-): BorderLeftStyle;
-export function borderLeft(
-  width: CSS.Property.BorderWidth<GriffelStylesCSSValue>,
-  style: CSS.Property.BorderStyle,
-  color: CSS.Property.BorderColor,
-): BorderLeftStyle;
+export function borderLeft(width: BorderWidthInput): BorderLeftStyle;
+export function borderLeft(width: BorderWidthInput, style: BorderStyleInput): BorderLeftStyle;
+export function borderLeft(width: BorderWidthInput, style: BorderStyleInput, color: BorderColorInput): BorderLeftStyle;
 
 /**
  * A function that implements expansion for "border-left", it's simplified - check usage examples.
@@ -24,9 +17,7 @@ export function borderLeft(
  *
  * See https://developer.mozilla.org/en-US/docs/Web/CSS/border-left
  */
-export function borderLeft(
-  ...values: [CSS.Property.BorderWidth<GriffelStylesCSSValue>, CSS.Property.BorderStyle?, CSS.Property.BorderColor?]
-): BorderLeftStyle {
+export function borderLeft(...values: [BorderWidthInput, BorderStyleInput?, BorderColorInput?]): BorderLeftStyle {
   return {
     borderLeftWidth: values[0],
     ...(values[1] && ({ borderLeftStyle: values[1] } as GriffelStylesStrictCSSObject)),
diff --git a/packages/core/src/shorthands/borderRadius.ts b/packages/core/src/shorthands/borderRadius.ts
index 26e332a..66d3243 100644
--- a/packages/core/src/shorthands/borderRadius.ts
+++ b/packages/core/src/shorthands/borderRadius.ts
@@ -1,4 +1,5 @@
-import type { GriffelStylesStrictCSSObject, GriffelStylesCSSValue } from '../types';
+import type { GriffelStylesStrictCSSObject } from '../types';
+import { BorderRadiusInput } from './types';
 
 type BorderRadiusStyle = Pick<
   GriffelStylesStrictCSSObject,
@@ -18,10 +19,10 @@ type BorderRadiusStyle = Pick<
  * See https://developer.mozilla.org/en-US/docs/Web/CSS/border-radius
  */
 export function borderRadius(
-  value1: GriffelStylesCSSValue,
-  value2: GriffelStylesCSSValue = value1,
-  value3: GriffelStylesCSSValue = value1,
-  value4: GriffelStylesCSSValue = value2,
+  value1: BorderRadiusInput,
+  value2: BorderRadiusInput = value1,
+  value3: BorderRadiusInput = value1,
+  value4: BorderRadiusInput = value2,
 ): BorderRadiusStyle {
   return {
     borderBottomRightRadius: value3,
diff --git a/packages/core/src/shorthands/borderRight.ts b/packages/core/src/shorthands/borderRight.ts
index 622a8bd..a436966 100644
--- a/packages/core/src/shorthands/borderRight.ts
+++ b/packages/core/src/shorthands/borderRight.ts
@@ -1,20 +1,17 @@
-import * as CSS from 'csstype';
-import type { GriffelStylesStrictCSSObject, GriffelStylesCSSValue } from '../types';
+import type { GriffelStylesStrictCSSObject } from '../types';
+import { BorderColorInput, BorderStyleInput, BorderWidthInput } from './types';
 
 type BorderRightStyle = Pick<
   GriffelStylesStrictCSSObject,
   'borderRightWidth' | 'borderRightStyle' | 'borderRightColor'
 >;
 
-export function borderRight(width: CSS.Property.BorderWidth<GriffelStylesCSSValue>): BorderRightStyle;
+export function borderRight(width: BorderWidthInput): BorderRightStyle;
+export function borderRight(width: BorderWidthInput, style: BorderStyleInput): BorderRightStyle;
 export function borderRight(
-  width: CSS.Property.BorderWidth<GriffelStylesCSSValue>,
-  style: CSS.Property.BorderStyle,
-): BorderRightStyle;
-export function borderRight(
-  width: CSS.Property.BorderWidth<GriffelStylesCSSValue>,
-  style: CSS.Property.BorderStyle,
-  color: CSS.Property.BorderColor,
+  width: BorderWidthInput,
+  style: BorderStyleInput,
+  color: BorderColorInput,
 ): BorderRightStyle;
 
 /**
@@ -27,9 +24,7 @@ export function borderRight(
  *
  * See https://developer.mozilla.org/en-US/docs/Web/CSS/border-right
  */
-export function borderRight(
-  ...values: [CSS.Property.BorderWidth<GriffelStylesCSSValue>, CSS.Property.BorderStyle?, CSS.Property.BorderColor?]
-): BorderRightStyle {
+export function borderRight(...values: [BorderWidthInput, BorderStyleInput?, BorderColorInput?]): BorderRightStyle {
   return {
     borderRightWidth: values[0],
     ...(values[1] && ({ borderRightStyle: values[1] } as BorderRightStyle)),
diff --git a/packages/core/src/shorthands/borderStyle.ts b/packages/core/src/shorthands/borderStyle.ts
index b330d06..7274976 100644
--- a/packages/core/src/shorthands/borderStyle.ts
+++ b/packages/core/src/shorthands/borderStyle.ts
@@ -1,25 +1,24 @@
-import * as CSS from 'csstype';
-
 import type { GriffelStylesStrictCSSObject } from '../types';
 import { generateStyles } from './generateStyles';
+import { BorderStyleInput } from './types';
 
 type BorderStyleStyle = Pick<
   GriffelStylesStrictCSSObject,
   'borderTopStyle' | 'borderRightStyle' | 'borderBottomStyle' | 'borderLeftStyle'
 >;
 
-export function borderStyle(all: CSS.Property.BorderStyle): BorderStyleStyle;
-export function borderStyle(vertical: CSS.Property.BorderStyle, horizontal: CSS.Property.BorderStyle): BorderStyleStyle;
+export function borderStyle(all: BorderStyleInput): BorderStyleStyle;
+export function borderStyle(vertical: BorderStyleInput, horizontal: BorderStyleInput): BorderStyleStyle;
 export function borderStyle(
-  top: CSS.Property.BorderStyle,
-  horizontal: CSS.Property.BorderStyle,
-  bottom: CSS.Property.BorderStyle,
+  top: BorderStyleInput,
+  horizontal: BorderStyleInput,
+  bottom: BorderStyleInput,
 ): BorderStyleStyle;
 export function borderStyle(
-  top: CSS.Property.BorderStyle,
-  right: CSS.Property.BorderStyle,
-  bottom: CSS.Property.BorderStyle,
-  left: CSS.Property.BorderStyle,
+  top: BorderStyleInput,
+  right: BorderStyleInput,
+  bottom: BorderStyleInput,
+  left: BorderStyleInput,
 ): BorderStyleStyle;
 
 /**
@@ -33,6 +32,6 @@ export function borderStyle(
  *
  * See https://developer.mozilla.org/en-US/docs/Web/CSS/border-style
  */
-export function borderStyle(...values: CSS.Property.BorderStyle[]): BorderStyleStyle {
+export function borderStyle(...values: BorderStyleInput[]): BorderStyleStyle {
   return generateStyles<BorderStyleStyle>('border', 'Style', ...values);
 }
diff --git a/packages/core/src/shorthands/borderTop.ts b/packages/core/src/shorthands/borderTop.ts
index 46e17f2..2e8e0e7 100644
--- a/packages/core/src/shorthands/borderTop.ts
+++ b/packages/core/src/shorthands/borderTop.ts
@@ -1,18 +1,11 @@
-import * as CSS from 'csstype';
-import type { GriffelStylesStrictCSSObject, GriffelStylesCSSValue } from '../types';
+import type { GriffelStylesStrictCSSObject } from '../types';
+import { BorderColorInput, BorderStyleInput, BorderWidthInput } from './types';
 
 type BorderTopStyle = Pick<GriffelStylesStrictCSSObject, 'borderTopWidth' | 'borderTopStyle' | 'borderTopColor'>;
 
-export function borderTop(width: CSS.Property.BorderWidth<GriffelStylesCSSValue>): BorderTopStyle;
-export function borderTop(
-  width: CSS.Property.BorderWidth<GriffelStylesCSSValue>,
-  style: CSS.Property.BorderStyle,
-): BorderTopStyle;
-export function borderTop(
-  width: CSS.Property.BorderWidth<GriffelStylesCSSValue>,
-  style: CSS.Property.BorderStyle,
-  color: CSS.Property.BorderColor,
-): BorderTopStyle;
+export function borderTop(width: BorderWidthInput): BorderTopStyle;
+export function borderTop(width: BorderWidthInput, style: BorderStyleInput): BorderTopStyle;
+export function borderTop(width: BorderWidthInput, style: BorderStyleInput, color: BorderColorInput): BorderTopStyle;
 
 /**
  * A function that implements expansion for "border-top", it's simplified - check usage examples.
@@ -24,9 +17,7 @@ export function borderTop(
  *
  * See https://developer.mozilla.org/en-US/docs/Web/CSS/border-top
  */
-export function borderTop(
-  ...values: [CSS.Property.BorderWidth<GriffelStylesCSSValue>, CSS.Property.BorderStyle?, CSS.Property.BorderColor?]
-): BorderTopStyle {
+export function borderTop(...values: [BorderWidthInput, BorderStyleInput?, BorderColorInput?]): BorderTopStyle {
   return {
     borderTopWidth: values[0],
     ...(values[1] && ({ borderTopStyle: values[1] } as BorderTopStyle)),
diff --git a/packages/core/src/shorthands/borderWidth.ts b/packages/core/src/shorthands/borderWidth.ts
index d223742..4a4217c 100644
--- a/packages/core/src/shorthands/borderWidth.ts
+++ b/packages/core/src/shorthands/borderWidth.ts
@@ -1,28 +1,24 @@
-import * as CSS from 'csstype';
-
-import type { GriffelStylesStrictCSSObject, GriffelStylesCSSValue } from '../types';
+import type { GriffelStylesStrictCSSObject } from '../types';
 import { generateStyles } from './generateStyles';
+import { BorderWidthInput } from './types';
 
 type BorderWidthStyle = Pick<
   GriffelStylesStrictCSSObject,
   'borderTopStyle' | 'borderRightStyle' | 'borderBottomStyle' | 'borderLeftStyle'
 >;
 
-export function borderWidth(all: CSS.Property.BorderWidth<GriffelStylesCSSValue>): BorderWidthStyle;
-export function borderWidth(
-  vertical: CSS.Property.BorderWidth<GriffelStylesCSSValue>,
-  horizontal: CSS.Property.BorderWidth<GriffelStylesCSSValue>,
-): BorderWidthStyle;
+export function borderWidth(all: BorderWidthInput): BorderWidthStyle;
+export function borderWidth(vertical: BorderWidthInput, horizontal: BorderWidthInput): BorderWidthStyle;
 export function borderWidth(
-  top: CSS.Property.BorderWidth<GriffelStylesCSSValue>,
-  horizontal: CSS.Property.BorderWidth<GriffelStylesCSSValue>,
-  bottom: CSS.Property.BorderWidth<GriffelStylesCSSValue>,
+  top: BorderWidthInput,
+  horizontal: BorderWidthInput,
+  bottom: BorderWidthInput,
 ): BorderWidthStyle;
 export function borderWidth(
-  top: CSS.Property.BorderWidth<GriffelStylesCSSValue>,
-  right: CSS.Property.BorderWidth<GriffelStylesCSSValue>,
-  bottom: CSS.Property.BorderWidth<GriffelStylesCSSValue>,
-  left: CSS.Property.BorderWidth<GriffelStylesCSSValue>,
+  top: BorderWidthInput,
+  right: BorderWidthInput,
+  bottom: BorderWidthInput,
+  left: BorderWidthInput,
 ): BorderWidthStyle;
 
 /**
@@ -36,6 +32,6 @@ export function borderWidth(
  *
  * See https://developer.mozilla.org/en-US/docs/Web/CSS/border-width
  */
-export function borderWidth(...values: CSS.Property.BorderWidth<GriffelStylesCSSValue>[]): BorderWidthStyle {
+export function borderWidth(...values: BorderWidthInput[]): BorderWidthStyle {
   return generateStyles<BorderWidthStyle>('border', 'Width', ...values);
 }
diff --git a/packages/core/src/shorthands/gap.ts b/packages/core/src/shorthands/gap.ts
index de11cfa..0c7e667 100644
--- a/packages/core/src/shorthands/gap.ts
+++ b/packages/core/src/shorthands/gap.ts
@@ -1,4 +1,5 @@
-import type { GriffelStylesStrictCSSObject, GriffelStylesCSSValue } from '../types';
+import type { GriffelStylesStrictCSSObject } from '../types';
+import { GapInput } from './types';
 
 type GapStyle = Pick<GriffelStylesStrictCSSObject, 'columnGap' | 'rowGap'>;
 
@@ -11,7 +12,7 @@ type GapStyle = Pick<GriffelStylesStrictCSSObject, 'columnGap' | 'rowGap'>;
  *
  * See https://developer.mozilla.org/en-US/docs/Web/CSS/gap
  */
-export function gap(columnGap: GriffelStylesCSSValue, rowGap: GriffelStylesCSSValue = columnGap): GapStyle {
+export function gap(columnGap: GapInput, rowGap: GapInput = columnGap): GapStyle {
   return {
     columnGap,
     rowGap,
diff --git a/packages/core/src/shorthands/margin.ts b/packages/core/src/shorthands/margin.ts
index f334206..d0ac501 100644
--- a/packages/core/src/shorthands/margin.ts
+++ b/packages/core/src/shorthands/margin.ts
@@ -1,21 +1,13 @@
-import type { GriffelStylesStrictCSSObject, GriffelStylesCSSValue } from '../types';
+import type { GriffelStylesStrictCSSObject } from '../types';
 import { generateStyles } from './generateStyles';
+import { MarginInput } from './types';
 
 type MarginStyle = Pick<GriffelStylesStrictCSSObject, 'marginTop' | 'marginRight' | 'marginBottom' | 'marginLeft'>;
 
-export function margin(all: GriffelStylesCSSValue): MarginStyle;
-export function margin(vertical: GriffelStylesCSSValue, horizontal: GriffelStylesCSSValue): MarginStyle;
-export function margin(
-  top: GriffelStylesCSSValue,
-  horizontal: GriffelStylesCSSValue,
-  bottom: GriffelStylesCSSValue,
-): MarginStyle;
-export function margin(
-  top: GriffelStylesCSSValue,
-  right: GriffelStylesCSSValue,
-  bottom: GriffelStylesCSSValue,
-  left: GriffelStylesCSSValue,
-): MarginStyle;
+export function margin(all: MarginInput): MarginStyle;
+export function margin(vertical: MarginInput, horizontal: MarginInput): MarginStyle;
+export function margin(top: MarginInput, horizontal: MarginInput, bottom: MarginInput): MarginStyle;
+export function margin(top: MarginInput, right: MarginInput, bottom: MarginInput, left: MarginInput): MarginStyle;
 
 /**
  * A function that implements CSS spec conformant expansion for "margin"
@@ -28,6 +20,6 @@ export function margin(
  *
  * See https://developer.mozilla.org/en-US/docs/Web/CSS/margin
  */
-export function margin(...values: GriffelStylesCSSValue[]): MarginStyle {
+export function margin(...values: MarginInput[]): MarginStyle {
   return generateStyles<MarginStyle>('margin', '', ...values);
 }
diff --git a/packages/core/src/shorthands/overflow.ts b/packages/core/src/shorthands/overflow.ts
index d40233d..c4667c6 100644
--- a/packages/core/src/shorthands/overflow.ts
+++ b/packages/core/src/shorthands/overflow.ts
@@ -1,5 +1,5 @@
-import * as CSS from 'csstype';
 import type { GriffelStylesStrictCSSObject } from '../types';
+import { OverflowInput } from './types';
 
 type OverflowStyle = Pick<GriffelStylesStrictCSSObject, 'overflowX' | 'overflowY'>;
 
@@ -12,10 +12,7 @@ type OverflowStyle = Pick<GriffelStylesStrictCSSObject, 'overflowX' | 'overflowY
  *
  * See https://developer.mozilla.org/en-US/docs/Web/CSS/overflow
  */
-export function overflow(
-  overflowX: CSS.Property.Overflow,
-  overflowY: CSS.Property.Overflow = overflowX,
-): OverflowStyle {
+export function overflow(overflowX: OverflowInput, overflowY: OverflowInput = overflowX): OverflowStyle {
   return {
     overflowX,
     overflowY,
diff --git a/packages/core/src/shorthands/padding.ts b/packages/core/src/shorthands/padding.ts
index 0dcb790..d3cd370 100644
--- a/packages/core/src/shorthands/padding.ts
+++ b/packages/core/src/shorthands/padding.ts
@@ -1,21 +1,13 @@
-import type { GriffelStylesStrictCSSObject, GriffelStylesCSSValue } from '../types';
+import type { GriffelStylesStrictCSSObject } from '../types';
 import { generateStyles } from './generateStyles';
+import { PaddingInput } from './types';
 
 type PaddingStyle = Pick<GriffelStylesStrictCSSObject, 'paddingTop' | 'paddingRight' | 'paddingBottom' | 'paddingLeft'>;
 
-export function padding(all: GriffelStylesCSSValue): PaddingStyle;
-export function padding(vertical: GriffelStylesCSSValue, horizontal: GriffelStylesCSSValue): PaddingStyle;
-export function padding(
-  top: GriffelStylesCSSValue,
-  horizontal: GriffelStylesCSSValue,
-  bottom: GriffelStylesCSSValue,
-): PaddingStyle;
-export function padding(
-  top: GriffelStylesCSSValue,
-  right: GriffelStylesCSSValue,
-  bottom: GriffelStylesCSSValue,
-  left: GriffelStylesCSSValue,
-): PaddingStyle;
+export function padding(all: PaddingInput): PaddingStyle;
+export function padding(vertical: PaddingInput, horizontal: PaddingInput): PaddingStyle;
+export function padding(top: PaddingInput, horizontal: PaddingInput, bottom: PaddingInput): PaddingStyle;
+export function padding(top: PaddingInput, right: PaddingInput, bottom: PaddingInput, left: PaddingInput): PaddingStyle;
 
 /**
  * A function that implements CSS spec conformant expansion for "padding"
@@ -28,6 +20,6 @@ export function padding(
  *
  * See https://developer.mozilla.org/en-US/docs/Web/CSS/padding
  */
-export function padding(...values: GriffelStylesCSSValue[]) {
+export function padding(...values: PaddingInput[]) {
   return generateStyles<PaddingStyle>('padding', '', ...values);
 }
diff --git a/packages/core/src/shorthands/types.ts b/packages/core/src/shorthands/types.ts
new file mode 100644
index 0000000..3b79852
--- /dev/null
+++ b/packages/core/src/shorthands/types.ts
@@ -0,0 +1,16 @@
+import * as CSS from 'csstype';
+import { GriffelStylesCSSValue, ValueOrArray } from '../types';
+
+export type BorderWidthInput = ValueOrArray<CSS.Property.BorderWidth<GriffelStylesCSSValue>>;
+export type BorderStyleInput = ValueOrArray<CSS.Property.BorderStyle>;
+export type BorderColorInput = ValueOrArray<CSS.Property.BorderColor>;
+
+export type BorderRadiusInput = ValueOrArray<CSS.Property.BorderRadius<GriffelStylesCSSValue>>;
+
+export type GapInput = ValueOrArray<CSS.Property.Gap<GriffelStylesCSSValue>>;
+
+export type MarginInput = ValueOrArray<CSS.Property.Margin<GriffelStylesCSSValue>>;
+
+export type OverflowInput = ValueOrArray<CSS.Property.Overflow>;
+
+export type PaddingInput = ValueOrArray<CSS.Property.Padding<GriffelStylesCSSValue>>;
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index f4efccb..401a054 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -5,8 +5,10 @@ export type GriffelStylesCSSValue = string | 0;
 
 export type GriffelStylesUnsupportedCSSProperties = Record<keyof typeof UNSUPPORTED_CSS_PROPERTIES, never>;
 
+export type ValueOrArray<T> = T | Array<T>;
+
 type GriffelStylesCSSProperties = Omit<
-  CSS.Properties<GriffelStylesCSSValue>,
+  CSS.PropertiesFallback<GriffelStylesCSSValue>,
   // We have custom definition for "animationName"
   'animationName'
 > &
@@ -19,8 +21,8 @@ export type GriffelStylesStrictCSSObject = GriffelStylesCSSProperties &
 
 type GriffelStylesCSSPseudos = {
   [Property in CSS.Pseudos]?:
-    | (GriffelStylesStrictCSSObject & { content?: string })
-    | (GriffelStylesCSSObjectCustomL1 & { content?: string });
+    | (GriffelStylesStrictCSSObject & { content?: string | string[] })
+    | (GriffelStylesCSSObjectCustomL1 & { content?: string | string[] });
 };
 
 //
@@ -30,23 +32,23 @@ type GriffelStylesCSSPseudos = {
 //
 
 type GriffelStylesCSSObjectCustomL1 = {
-  [Property: string]: string | number | undefined | GriffelStylesCSSObjectCustomL2;
+  [Property: string]: string | number | (string | number)[] | undefined | GriffelStylesCSSObjectCustomL2;
 } & GriffelStylesStrictCSSObject;
 
 type GriffelStylesCSSObjectCustomL2 = {
-  [Property: string]: string | number | undefined | GriffelStylesCSSObjectCustomL3;
+  [Property: string]: string | number | (string | number)[] | undefined | GriffelStylesCSSObjectCustomL3;
 } & GriffelStylesStrictCSSObject;
 
 type GriffelStylesCSSObjectCustomL3 = {
-  [Property: string]: string | number | undefined | GriffelStylesCSSObjectCustomL4;
+  [Property: string]: string | number | (string | number)[] | undefined | GriffelStylesCSSObjectCustomL4;
 } & GriffelStylesStrictCSSObject;
 
 type GriffelStylesCSSObjectCustomL4 = {
-  [Property: string]: string | number | undefined | GriffelStylesCSSObjectCustomL5;
+  [Property: string]: string | number | (string | number)[] | undefined | GriffelStylesCSSObjectCustomL5;
 } & GriffelStylesStrictCSSObject;
 
 type GriffelStylesCSSObjectCustomL5 = {
-  [Property: string]: string | number | undefined | GriffelStylesStrictCSSObject;
+  [Property: string]: string | number | (string | number)[] | undefined | GriffelStylesStrictCSSObject;
 } & GriffelStylesStrictCSSObject;
 
 export type GriffelStyle = GriffelStylesStrictCSSObject | GriffelStylesCSSObjectCustomL1;
diff --git a/packages/jest-serializer/CHANGELOG.json b/packages/jest-serializer/CHANGELOG.json
index 8bb5650..66b1436 100644
--- a/packages/jest-serializer/CHANGELOG.json
+++ b/packages/jest-serializer/CHANGELOG.json
@@ -1,6 +1,29 @@
 {
   "name": "@griffel/jest-serializer",
   "entries": [
+    {
+      "date": "Wed, 06 Apr 2022 13:28:28 GMT",
+      "tag": "@griffel/jest-serializer_v1.0.3",
+      "version": "1.0.3",
+      "comments": {
+        "none": [
+          {
+            "author": "olfedias@microsoft.com",
+            "package": "@griffel/jest-serializer",
+            "commit": "cbfd3045dadcce6f2f8348d56eaf882c5e408482",
+            "comment": "update README, fix comments"
+          }
+        ],
+        "patch": [
+          {
+            "author": "beachball",
+            "package": "@griffel/jest-serializer",
+            "comment": "Bump @griffel/core to v1.2.0",
+            "commit": "995c356bc77272f6fe69a604db6485d57a9ba7c4"
+          }
+        ]
+      }
+    },
     {
       "date": "Wed, 09 Mar 2022 13:56:06 GMT",
       "tag": "@griffel/jest-serializer_v1.0.2",
diff --git a/packages/jest-serializer/CHANGELOG.md b/packages/jest-serializer/CHANGELOG.md
index 6ec7d0b..03a4674 100644
--- a/packages/jest-serializer/CHANGELOG.md
+++ b/packages/jest-serializer/CHANGELOG.md
@@ -1,9 +1,17 @@
 # Change Log - @griffel/jest-serializer
 
-This log was last generated on Wed, 09 Mar 2022 13:56:06 GMT and should not be manually modified.
+This log was last generated on Wed, 06 Apr 2022 13:28:28 GMT and should not be manually modified.
 
 <!-- Start content -->
 
+## 1.0.3
+
+Wed, 06 Apr 2022 13:28:28 GMT
+
+### Patches
+
+- Bump @griffel/core to v1.2.0
+
 ## 1.0.2
 
 Wed, 09 Mar 2022 13:56:06 GMT
diff --git a/packages/jest-serializer/package.json b/packages/jest-serializer/package.json
index 5c95e4b..a2d23e7 100644
--- a/packages/jest-serializer/package.json
+++ b/packages/jest-serializer/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@griffel/jest-serializer",
-  "version": "1.0.2",
+  "version": "1.0.3",
   "description": "Jest serializer for Griffel",
   "license": "MIT",
   "repository": {
@@ -11,6 +11,6 @@
     "tslib": "^2.1.0"
   },
   "peerDependencies": {
-    "@griffel/core": "^1.1.1"
+    "@griffel/core": "^1.2.0"
   }
 }
diff --git a/packages/react/CHANGELOG.json b/packages/react/CHANGELOG.json
index e4f274b..da5fe0c 100644
--- a/packages/react/CHANGELOG.json
+++ b/packages/react/CHANGELOG.json
@@ -1,6 +1,35 @@
 {
   "name": "@griffel/react",
   "entries": [
+    {
+      "date": "Wed, 06 Apr 2022 13:28:28 GMT",
+      "tag": "@griffel/react_v1.0.3",
+      "version": "1.0.3",
+      "comments": {
+        "none": [
+          {
+            "author": "miroslav.stastny@microsoft.com",
+            "package": "@griffel/react",
+            "commit": "05635e139c200e609ddb531f40e144b65f9cdcf3",
+            "comment": "Fallback values stories"
+          },
+          {
+            "author": "olfedias@microsoft.com",
+            "package": "@griffel/react",
+            "commit": "2bda4e578a4fcdd1751a3e7496ea9a6df7916012",
+            "comment": "docs: add docs for createDOMRenderer"
+          }
+        ],
+        "patch": [
+          {
+            "author": "beachball",
+            "package": "@griffel/react",
+            "comment": "Bump @griffel/core to v1.2.0",
+            "commit": "995c356bc77272f6fe69a604db6485d57a9ba7c4"
+          }
+        ]
+      }
+    },
     {
       "date": "Wed, 09 Mar 2022 13:56:06 GMT",
       "tag": "@griffel/react_v1.0.2",
diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md
index 4cf096d..71b7c54 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, 09 Mar 2022 13:56:06 GMT and should not be manually modified.
+This log was last generated on Wed, 06 Apr 2022 13:28:28 GMT and should not be manually modified.
 
 <!-- Start content -->
 
+## 1.0.3
+
+Wed, 06 Apr 2022 13:28:28 GMT
+
+### Patches
+
+- Bump @griffel/core to v1.2.0
+
 ## 1.0.2
 
 Wed, 09 Mar 2022 13:56:06 GMT
diff --git a/packages/react/README.md b/packages/react/README.md
index 0587d98..b7534c3 100644
--- a/packages/react/README.md
+++ b/packages/react/README.md
@@ -236,3 +236,18 @@ const useClasses = makeStyles({
   },
 });
 ```
+
+### CSS Fallback Properties
+
+Any CSS property accepts an array of values which are all added to the styles.
+Every browser will use the latest valid value (which might be a different one in different browsers, based on supported CSS in that browser):
+
+```js
+import { makeStyles } from '@griffel/react';
+
+const useClasses = makeStyles({
+  root: {
+    overflowY: ['scroll', 'overlay'],
+  },
+});
+```
diff --git a/packages/react/package.json b/packages/react/package.json
index 75dc122..d6be1dc 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@griffel/react",
-  "version": "1.0.2",
+  "version": "1.0.3",
   "description": "React implementation of Atomic CSS-in-JS",
   "license": "MIT",
   "repository": {
@@ -9,7 +9,7 @@
   },
   "sideEffects": false,
   "dependencies": {
-    "@griffel/core": "^1.1.1",
+    "@griffel/core": "^1.2.0",
     "tslib": "^2.1.0"
   },
   "peerDependencies": {
diff --git a/packages/react/project.json b/packages/react/project.json
index 7865deb..551ea7b 100644
--- a/packages/react/project.json
+++ b/packages/react/project.json
@@ -57,7 +57,7 @@
         "cwd": "packages/react",
         "commands": [
           {
-            "command": "bundle-size measure"
+            "command": "monosize measure"
           }
         ]
       }
diff --git a/packages/react/src/stories/FallbackValues.stories.tsx b/packages/react/src/stories/FallbackValues.stories.tsx
new file mode 100644
index 0000000..92e2b05
--- /dev/null
+++ b/packages/react/src/stories/FallbackValues.stories.tsx
@@ -0,0 +1,47 @@
+import * as React from 'react';
+import { Story } from '@storybook/react';
+
+import { makeStyles, shorthands, TextDirectionProvider } from '../';
+
+const useStyles = makeStyles({
+  shouldFallbackToRed: {
+    color: ['red', 'invalid'],
+    ...shorthands.border(['1px'], 'solid', ['red', 'invalid']),
+    ...shorthands.borderRight('40px', 'solid', ['green', 'invalid']),
+    borderTopStyle: ['hidden', 'dashed'],
+  },
+  shouldBeBlue: {
+    color: ['red', 'blue'],
+    ...shorthands.border('1px', 'solid', ['red', 'blue']),
+    ...shorthands.borderRight('40px', 'solid', ['green', 'grey']),
+    borderRightWidth: '100px',
+  },
+});
+
+const FallbackTest = ({ isRtl = false }) => {
+  const classes = useStyles();
+  return (
+    <>
+      <p className={classes.shouldBeBlue}>
+        Colour and border should be blue, {isRtl ? 'left' : 'right'} wide border grey.
+      </p>
+      <p className={classes.shouldFallbackToRed}>
+        Colour and border should fallback to red, {isRtl ? 'left' : 'right'} wide border to green.
+      </p>
+    </>
+  );
+};
+
+export const FallbackValues: Story = () => {
+  return (
+    <div>
+      <FallbackTest />
+      <TextDirectionProvider dir="rtl">
+        <div dir="rtl">
+          <h2>RTL</h2>
+          <FallbackTest isRtl />
+        </div>
+      </TextDirectionProvider>
+    </div>
+  );
+};
diff --git a/packages/react/src/stories/index.stories.tsx b/packages/react/src/stories/index.stories.tsx
index fa601e9..1a7448f 100644
--- a/packages/react/src/stories/index.stories.tsx
+++ b/packages/react/src/stories/index.stories.tsx
@@ -1,5 +1,7 @@
 export { Default } from './makeStyles.stories';
 export { ComponentStyles } from './ComponentStyles.stories';
+export { FallbackValues } from './FallbackValues.stories';
+
 export { DOMRendererFilter } from './DOMRendererFilter.stories';
 
 export default {
diff --git a/packages/webpack-loader/CHANGELOG.json b/packages/webpack-loader/CHANGELOG.json
index 5702662..8e03804 100644
--- a/packages/webpack-loader/CHANGELOG.json
+++ b/packages/webpack-loader/CHANGELOG.json
@@ -1,6 +1,41 @@
 {
   "name": "@griffel/webpack-loader",
   "entries": [
+    {
+      "date": "Wed, 06 Apr 2022 13:28:28 GMT",
+      "tag": "@griffel/webpack-loader_v2.0.3",
+      "version": "2.0.3",
+      "comments": {
+        "none": [
+          {
+            "author": "olfedias@microsoft.com",
+            "package": "@griffel/webpack-loader",
+            "commit": "cbfd3045dadcce6f2f8348d56eaf882c5e408482",
+            "comment": "update README, fix caller & plugin names"
+          },
+          {
+            "author": "olfedias@microsoft.com",
+            "package": "@griffel/webpack-loader",
+            "commit": "594f3264239c1c138eeae969713ed09498e95b6c",
+            "comment": "docs: update README to include configuration guide"
+          }
+        ],
+        "patch": [
+          {
+            "author": "beachball",
+            "package": "@griffel/webpack-loader",
+            "comment": "Bump @griffel/babel-preset to v1.2.0",
+            "commit": "995c356bc77272f6fe69a604db6485d57a9ba7c4"
+          },
+          {
+            "author": "beachball",
+            "package": "@griffel/webpack-loader",
+            "comment": "Bump @griffel/react to v1.0.3",
+            "commit": "995c356bc77272f6fe69a604db6485d57a9ba7c4"
+          }
+        ]
+      }
+    },
     {
       "date": "Wed, 09 Mar 2022 13:56:06 GMT",
       "tag": "@griffel/webpack-loader_v2.0.2",
diff --git a/packages/webpack-loader/CHANGELOG.md b/packages/webpack-loader/CHANGELOG.md
index 4a26938..e06632e 100644
--- a/packages/webpack-loader/CHANGELOG.md
+++ b/packages/webpack-loader/CHANGELOG.md
@@ -1,9 +1,18 @@
 # Change Log - @griffel/webpack-loader
 
-This log was last generated on Wed, 09 Mar 2022 13:56:06 GMT and should not be manually modified.
+This log was last generated on Wed, 06 Apr 2022 13:28:28 GMT and should not be manually modified.
 
 <!-- Start content -->
 
+## 2.0.3
+
+Wed, 06 Apr 2022 13:28:28 GMT
+
+### Patches
+
+- Bump @griffel/babel-preset to v1.2.0
+- Bump @griffel/react to v1.0.3
+
 ## 2.0.2
 
 Wed, 09 Mar 2022 13:56:06 GMT
diff --git a/packages/webpack-loader/package.json b/packages/webpack-loader/package.json
index ed92a17..b72eb32 100644
--- a/packages/webpack-loader/package.json
+++ b/packages/webpack-loader/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@griffel/webpack-loader",
-  "version": "2.0.2",
+  "version": "2.0.3",
   "description": "Webpack loader that performs build time transforms for Griffel",
   "license": "MIT",
   "repository": {
@@ -9,7 +9,7 @@
   },
   "dependencies": {
     "@babel/core": "^7.12.13",
-    "@griffel/babel-preset": "^1.1.1",
+    "@griffel/babel-preset": "^1.2.0",
     "@linaria/babel-preset": "^3.0.0-beta.14",
     "enhanced-resolve": "^5.8.2",
     "loader-utils": "^2.0.0",
@@ -17,7 +17,7 @@
     "tslib": "^2.1.0"
   },
   "peerDependencies": {
-    "@griffel/react": "^1.0.2",
+    "@griffel/react": "^1.0.3",
     "webpack": "^5"
   }
 }
diff --git a/yarn.lock b/yarn.lock
index db0ff51..f14946d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -181,120 +181,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@azure/abort-controller@npm:^1.0.0":
-  version: 1.0.4
-  resolution: "@azure/abort-controller@npm:1.0.4"
-  dependencies:
-    tslib: ^2.0.0
-  checksum: 3bdb4d13505e91813729f053b5e50573aabcbb88cd7b187e8fd09aaf7a3ea8e2907ab6d6ee9bbe016060d312aecf189b2e1273e9fcc15bd93699632dcebafc06
-  languageName: node
-  linkType: hard
-
-"@azure/core-asynciterator-polyfill@npm:^1.0.0":
-  version: 1.0.0
-  resolution: "@azure/core-asynciterator-polyfill@npm:1.0.0"
-  checksum: b6d3ab3f0ffc0a9f426a1d2cc90b81778730b14d81af7dcab9760c9e241f7c0f6843d45fad572dcd1379232b3fb2cacd76d110a0d54a999ceb1bf0fd9ed39e99
-  languageName: node
-  linkType: hard
-
-"@azure/core-auth@npm:^1.3.0":
-  version: 1.3.2
-  resolution: "@azure/core-auth@npm:1.3.2"
-  dependencies:
-    "@azure/abort-controller": ^1.0.0
-    tslib: ^2.2.0
-  checksum: 8b27de6f8a82a63643524757ef774b1637b0d5c7769b387c73bebeb918717e7193ad1d2413638fb02b28de8199f261c1a902e3d04f75d1ef684af33529d9e98d
-  languageName: node
-  linkType: hard
-
-"@azure/core-client@npm:^1.0.0":
-  version: 1.4.0
-  resolution: "@azure/core-client@npm:1.4.0"
-  dependencies:
-    "@azure/abort-controller": ^1.0.0
-    "@azure/core-asynciterator-polyfill": ^1.0.0
-    "@azure/core-auth": ^1.3.0
-    "@azure/core-rest-pipeline": ^1.4.0
-    "@azure/core-tracing": 1.0.0-preview.13
-    "@azure/logger": ^1.0.0
-    tslib: ^2.2.0
-  checksum: 4639baffbbf6ff483ed2a3990af4444cd06ab6833acec70e75df8c86f4fdcbba8871b3eb9e57aa4bbeb0b9297c3de1acc3f84220c3ea91e05e75b4fdf6e0ac15
-  languageName: node
-  linkType: hard
-
-"@azure/core-paging@npm:^1.1.1":
-  version: 1.2.1
-  resolution: "@azure/core-paging@npm:1.2.1"
-  dependencies:
-    "@azure/core-asynciterator-polyfill": ^1.0.0
-    tslib: ^2.2.0
-  checksum: b4e07b1d9eb986f902f4a4741aac7e31b96373e0611aea74771c1eb25e1d38e9047a88af2643a17413e0304139337a457f03a465f1666b9affc5510cdf20fa2e
-  languageName: node
-  linkType: hard
-
-"@azure/core-rest-pipeline@npm:^1.1.0, @azure/core-rest-pipeline@npm:^1.4.0":
-  version: 1.4.0
-  resolution: "@azure/core-rest-pipeline@npm:1.4.0"
-  dependencies:
-    "@azure/abort-controller": ^1.0.0
-    "@azure/core-auth": ^1.3.0
-    "@azure/core-tracing": 1.0.0-preview.13
-    "@azure/logger": ^1.0.0
-    form-data: ^4.0.0
-    http-proxy-agent: ^4.0.1
-    https-proxy-agent: ^5.0.0
-    tslib: ^2.2.0
-    uuid: ^8.3.0
-  checksum: 63addce249281a11c23c3a22d2a7eb0cae8d7d89b5ee16a49cc6d85b5d3e9293e6d8051cf20c15ee1150af5b4295dc162d6a911f064f3f56d6122f2727145a24
-  languageName: node
-  linkType: hard
-
-"@azure/core-tracing@npm:1.0.0-preview.13":
-  version: 1.0.0-preview.13
-  resolution: "@azure/core-tracing@npm:1.0.0-preview.13"
-  dependencies:
-    "@opentelemetry/api": ^1.0.1
-    tslib: ^2.2.0
-  checksum: bc3ea8dce1fc6bb6e4cb2e82ec0c361b3e6f6e18e162f352eb347e6991c6461ebc249f5d1b36402cc0d295e2a6bcbaa68014445d7f4293c0792a698c430f145e
-  languageName: node
-  linkType: hard
-
-"@azure/core-xml@npm:^1.0.0":
-  version: 1.1.0
-  resolution: "@azure/core-xml@npm:1.1.0"
-  dependencies:
-    fast-xml-parser: ^3.20.0
-    tslib: ^2.2.0
-  checksum: 9e6e2f3c0ee6e46b3a2b46740a1c7756da4f4513f83e56b955f03d4c667968d1e095346c4f73a6bde14a62a25e46fb77b67db8a59d7d2c0c8788cfe79fe4e5ef
-  languageName: node
-  linkType: hard
-
-"@azure/data-tables@npm:^13.0.0":
-  version: 13.0.1
-  resolution: "@azure/data-tables@npm:13.0.1"
-  dependencies:
-    "@azure/core-auth": ^1.3.0
-    "@azure/core-client": ^1.0.0
-    "@azure/core-paging": ^1.1.1
-    "@azure/core-rest-pipeline": ^1.1.0
-    "@azure/core-tracing": 1.0.0-preview.13
-    "@azure/core-xml": ^1.0.0
-    "@azure/logger": ^1.0.0
-    tslib: ^2.2.0
-    uuid: ^8.3.0
-  checksum: 16cca1c2d0ecf36dd71730ae2a8e97cb0fae4ba0409984518fbcabbcad0bdb773f3087019f5d1e59b100b13b6ccf2c2772768e1399df062fca83afb7dd443900
-  languageName: node
-  linkType: hard
-
-"@azure/logger@npm:^1.0.0":
-  version: 1.0.3
-  resolution: "@azure/logger@npm:1.0.3"
-  dependencies:
-    tslib: ^2.2.0
-  checksum: f3443c70c678a7449a5f3be09488a0a15711c506c9da6a81fa9e43178128ddf5db3abc02c99359d188e4ef47d703c5e5f206df71b0e01884245de3220ab1205b
-  languageName: node
-  linkType: hard
-
 "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.16.7, @babel/code-frame@npm:^7.5.5, @babel/code-frame@npm:^7.8.3":
   version: 7.16.7
   resolution: "@babel/code-frame@npm:7.16.7"
@@ -2870,33 +2756,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@fluentui/bundle-size@npm:^1.1.4":
-  version: 1.1.4
-  resolution: "@fluentui/bundle-size@npm:1.1.4"
-  dependencies:
-    "@azure/data-tables": ^13.0.0
-    "@babel/core": ^7.10.4
-    ajv: ^8.4.0
-    chalk: ^4.1.0
-    ci-info: ^3.2.0
-    cli-table3: ^0.6.0
-    del: ^6.0.0
-    find-up: ^5.0.0
-    glob: ^7.1.2
-    gzip-size: ^6.0.0
-    node-fetch: ^2.6.1
-    prettier: ^2.2.1
-    pretty-bytes: ^5.6.0
-    terser: ^5.5.1
-    webpack: ^5.21.2
-    workspace-tools: ^0.16.2
-    yargs: ^13.3.2
-  bin:
-    bundle-size: bin/bundle-size.js
-  checksum: 90c28509ea19565f96aab68369ff45f2518de324b3c0eea0c9ba4e458dc2b5b21c6b2791cbbd3ee6e97ae8e30e0aca3a6e16e8ed678d491bcdd819c7be17cba5
-  languageName: node
-  linkType: hard
-
 "@gar/promisify@npm:^1.0.1":
   version: 1.1.2
   resolution: "@gar/promisify@npm:1.1.2"
@@ -4007,13 +3866,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@opentelemetry/api@npm:^1.0.1":
-  version: 1.0.4
-  resolution: "@opentelemetry/api@npm:1.0.4"
-  checksum: 793e9b5c21666b647a60c58c46c3e00ad1dac38505102b026ad0ef617571d637aca54a18533a73c1e288c95b5ac77e2db17f96467f11833ac1165338e1184260
-  languageName: node
-  linkType: hard
-
 "@parcel/watcher@npm:2.0.4":
   version: 2.0.4
   resolution: "@parcel/watcher@npm:2.0.4"
@@ -6783,6 +6635,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@upstash/redis@npm:^1.0.3":
+  version: 1.0.3
+  resolution: "@upstash/redis@npm:1.0.3"
+  dependencies:
+    isomorphic-fetch: ^3.0.0
+  checksum: c47443fd68e86aa32ebb606f77432f43d92da86b4f0eae9debac88481ce7f11eeac9d66f157d2f7894285f73f70fdbc18d075aacab7fca7548e1a595b4cd58e7
+  languageName: node
+  linkType: hard
+
 "@webassemblyjs/ast@npm:1.11.1":
   version: 1.11.1
   resolution: "@webassemblyjs/ast@npm:1.11.1"
@@ -12119,17 +11980,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"fast-xml-parser@npm:^3.20.0":
-  version: 3.21.1
-  resolution: "fast-xml-parser@npm:3.21.1"
-  dependencies:
-    strnum: ^1.0.4
-  bin:
-    xml2js: cli.js
-  checksum: 73b9c907a424cc2f9b11a8a2f1b7448d936f1db6fa574b85cbe4be9739c2f77d99a827bb27d738a0db0047b20c71a5d663f64937fbdb9c38977fc6cd145221d2
-  languageName: node
-  linkType: hard
-
 "fastq@npm:^1.6.0":
   version: 1.13.0
   resolution: "fastq@npm:1.13.0"
@@ -12550,17 +12400,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"form-data@npm:^4.0.0":
-  version: 4.0.0
-  resolution: "form-data@npm:4.0.0"
-  dependencies:
-    asynckit: ^0.4.0
-    combined-stream: ^1.0.8
-    mime-types: ^2.1.12
-  checksum: 01135bf8675f9d5c61ff18e2e2932f719ca4de964e3be90ef4c36aacfc7b9cb2fceb5eca0b7e0190e3383fe51c5b37f4cb80b62ca06a99aaabfcfd6ac7c9328c
-  languageName: node
-  linkType: hard
-
 "format@npm:^0.2.0":
   version: 0.2.2
   resolution: "format@npm:0.2.2"
@@ -13212,7 +13051,6 @@ __metadata:
     "@docusaurus/module-type-aliases": ^2.0.0-beta.15
     "@docusaurus/preset-classic": 2.0.0-beta.15
     "@emotion/hash": ^0.8.0
-    "@fluentui/bundle-size": ^1.1.4
     "@linaria/babel-preset": ^3.0.0-beta.14
     "@linaria/shaker": ^3.0.0-beta.14
     "@nrwl/cli": 13.4.5
@@ -13266,6 +13104,8 @@ __metadata:
     js-beautify: ^1.14.0
     loader-utils: ^2.0.0
     log-symbols: 5.1.0
+    monosize: 0.0.3
+    monosize-storage-upstash: 0.0.2
     nano-staged: 0.5.0
     prettier: 2.3.1
     prism-react-renderer: 1.2.1
@@ -14935,6 +14775,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"isomorphic-fetch@npm:^3.0.0":
+  version: 3.0.0
+  resolution: "isomorphic-fetch@npm:3.0.0"
+  dependencies:
+    node-fetch: ^2.6.1
+    whatwg-fetch: ^3.4.1
+  checksum: e5ab79a56ce5af6ddd21265f59312ad9a4bc5a72cebc98b54797b42cb30441d5c5f8d17c5cd84a99e18101c8af6f90c081ecb8d12fd79e332be1778d58486d75
+  languageName: node
+  linkType: hard
+
 "istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.0.1, istanbul-lib-coverage@npm:^3.2.0":
   version: 3.2.0
   resolution: "istanbul-lib-coverage@npm:3.2.0"
@@ -17081,6 +16931,68 @@ __metadata:
   languageName: node
   linkType: hard
 
+"monosize-storage-upstash@npm:0.0.2":
+  version: 0.0.2
+  resolution: "monosize-storage-upstash@npm:0.0.2"
+  dependencies:
+    "@upstash/redis": ^1.0.3
+    monosize: ^0.0.3
+  checksum: 0eb4ba1824ba956aa68ab0b1caf307175944710ccbaa51c0bb15593cef1d48b1ca1bb1442314c44c319fc54102be987f3a5851acc4470794c4ff5e177b5e10a0
+  languageName: node
+  linkType: hard
+
+"monosize@npm:0.0.3, monosize@npm:^0.0.3":
+  version: 0.0.3
+  resolution: "monosize@npm:0.0.3"
+  dependencies:
+    "@babel/core": ^7.10.4
+    ajv: ^8.4.0
+    chalk: ^4.1.0
+    ci-info: ^3.2.0
+    cli-table3: ^0.6.0
+    del: ^6.0.0
+    find-up: ^5.0.0
+    glob: ^7.1.2
+    gzip-size: ^6.0.0
+    prettier: ^2.5.1
+    pretty-bytes: ^5.6.0
+    terser: ^5.5.1
+    tslib: ^2.3.1
+    webpack: ^5.21.2
+    workspace-tools: ^0.16.2
+    yargs: ^13.3.2
+  bin:
+    monosize: bin/monosize.js
+  checksum: dcebc72dce530145c3c72a6e9e8445e2a2f544210c58b4db348cc672d7cd1a6171923eb6d8f6c7a8e9eac1e09ad9c2d8787ae46b3c6406f08ba3d89f0e6e6128
+  languageName: node
+  linkType: hard
+
+"monosize@patch:monosize@npm:0.0.3#.yarn/patches/monosize-npm-0.0.3-9b650c3f51::locator=griffel-repository%40workspace%3A.":
+  version: 0.0.3
+  resolution: "monosize@patch:monosize@npm%3A0.0.3#.yarn/patches/monosize-npm-0.0.3-9b650c3f51::version=0.0.3&hash=9d24b1&locator=griffel-repository%40workspace%3A."
+  dependencies:
+    "@babel/core": ^7.10.4
+    ajv: ^8.4.0
+    chalk: ^4.1.0
+    ci-info: ^3.2.0
+    cli-table3: ^0.6.0
+    del: ^6.0.0
+    find-up: ^5.0.0
+    glob: ^7.1.2
+    gzip-size: ^6.0.0
+    prettier: ^2.5.1
+    pretty-bytes: ^5.6.0
+    terser: ^5.5.1
+    tslib: ^2.3.1
+    webpack: ^5.21.2
+    workspace-tools: ^0.16.2
+    yargs: ^13.3.2
+  bin:
+    monosize: bin/monosize.js
+  checksum: 0f3b74aa270907c0a870d02a2bd508d906f2c7531ef162bbffb37df75e4a041f13f54e3456fce08eeb23156440f29c3d54ea7d9f4adda9a1a2f0e21bb70c774d
+  languageName: node
+  linkType: hard
+
 "move-concurrently@npm:^1.0.1":
   version: 1.0.1
   resolution: "move-concurrently@npm:1.0.1"
@@ -19075,12 +18987,12 @@ __metadata:
   languageName: node
   linkType: hard
 
-"prettier@npm:^2.0.1, prettier@npm:^2.2.1":
-  version: 2.5.1
-  resolution: "prettier@npm:2.5.1"
+"prettier@npm:^2.0.1, prettier@npm:^2.5.1":
+  version: 2.6.0
+  resolution: "prettier@npm:2.6.0"
   bin:
     prettier: bin-prettier.js
-  checksum: 21b9408476ea1c544b0e45d51ceb94a84789ff92095abb710942d780c862d0daebdb29972d47f6b4d0f7ebbfb0ffbf56cc2cfa3e3e9d1cca54864af185b15b66
+  checksum: 3e527ad62279676778a8404d18174d7ca2365ada4caba6eebbcdd9907d1187afd3bc6ade5b4e5f5d4549bb9fb71e45ca8930d71500017635524f8fc05bc52e93
   languageName: node
   linkType: hard
 
@@ -22051,13 +21963,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"strnum@npm:^1.0.4":
-  version: 1.0.5
-  resolution: "strnum@npm:1.0.5"
-  checksum: 651b2031db5da1bf4a77fdd2f116a8ac8055157c5420f5569f64879133825915ad461513e7202a16d7fec63c54fd822410d0962f8ca12385c4334891b9ae6dd2
-  languageName: node
-  linkType: hard
-
 "style-inject@npm:^0.3.0":
   version: 0.3.0
   resolution: "style-inject@npm:0.3.0"
@@ -23528,7 +23433,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"uuid@npm:^8.3.0, uuid@npm:^8.3.1, uuid@npm:^8.3.2":
+"uuid@npm:^8.3.1, uuid@npm:^8.3.2":
   version: 8.3.2
   resolution: "uuid@npm:8.3.2"
   bin:
@@ -24076,6 +23981,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"whatwg-fetch@npm:^3.4.1":
+  version: 3.6.2
+  resolution: "whatwg-fetch@npm:3.6.2"
+  checksum: ee976b7249e7791edb0d0a62cd806b29006ad7ec3a3d89145921ad8c00a3a67e4be8f3fb3ec6bc7b58498724fd568d11aeeeea1f7827e7e1e5eae6c8a275afed
+  languageName: node
+  linkType: hard
+
 "whatwg-mimetype@npm:^2.3.0":
   version: 2.3.0
   resolution: "whatwg-mimetype@npm:2.3.0"


Test patch:
diff --git a/e2e/typescript/src/assets/fixture.ts b/e2e/typescript/src/assets/fixture.ts
index bbdb112..c464693 100644
--- a/e2e/typescript/src/assets/fixture.ts
+++ b/e2e/typescript/src/assets/fixture.ts
@@ -134,8 +134,8 @@ assertType({
   boxSizing: 'outline-box',
 });
 assertType({
-  // @ts-expect-error "1" is invalid value for "overflow"
-  overflow: '1',
+  // @ts-expect-error "1" is invalid value for "overflowX"
+  overflowX: '1',
 });
 assertType({
   // @ts-expect-error "paddingLeft" cannot be numeric value
@@ -188,13 +188,31 @@ assertType({
   color: 'var(--customColor)',
 });
 
+assertType({
+  '@media screen and (max-width: 992px)': {
+    ...typedMixin,
+  },
+  // @ts-expect-error FIXME - this is supposed to pass!
+  '@media(forced-colors: active)': {
+    ...typedMixin,
+    color: 'var(--customColor)',
+  },
+  ':after': {
+    ...typedMixin,
+  },
+  ':before': {
+    ...typedMixin,
+    color: 'var(--customColor)',
+  },
+});
+
 // Strict selectors
 //
 
 assertType({
   ':hover': {
-    // @ts-expect-error "1" is invalid value for "overflow"
-    overflow: '1',
+    // @ts-expect-error "1" is invalid value for "overflowX"
+    overflowX: '1',
     // @ts-expect-error "padding" is banned
     padding: 0,
     // @ts-expect-error "paddingLeft" cannot be numeric value
@@ -213,21 +231,45 @@ assertType({
 //
 
 assertType({
+  // @ts-expect-error "1" is invalid value for "overflowX"
+  ':hover:focus': {
+    overflowX: '1',
+  },
+});
+
+assertType({
+  // @ts-expect-error "padding" is banned
   ':hover:focus': {
-    // @ts-expect-error "1" is invalid value for "overflow"
-    overflow: '1',
-    // @ts-expect-error "padding" is banned
     padding: 0,
-    // @ts-expect-error "paddingLeft" cannot be numeric value
+  },
+});
+
+assertType({
+  // @ts-expect-error "paddingLeft" cannot be numeric value
+  ':hover:focus': {
     paddingLeft: 5,
   },
 });
+
+assertType({
+  // @ts-expect-error "1" is invalid value for "overflowX", padding is banned, paddingLeft cannot be a numeric value
+  ':hover:focus': {
+    overflowX: 'scroll',
+    padding: 0,
+    paddingLeft: 5,
+  },
+});
+
 assertType({
   ':hover:focus': {
     // @ts-expect-error Object is not assignable to CSS property
     zIndex: { color: 'red' },
+    opacity: { color: 'red' }, // < no error here, TS only reports the first error in this case
+  },
+  ':hover:active': {
     // @ts-expect-error Object is not assignable to CSS property
     opacity: { color: 'red' },
+    zIndex: { color: 'red' }, // < no error here, TS only reports the first error in this case
   },
 });
 
@@ -235,37 +277,33 @@ assertType({
 //
 
 assertType({
+  // @ts-expect-error "1" is invalid value for "overflowX", padding is banned, paddingLeft cannot be a numeric value
   '.foo': {
     '.baz': {
-      // @ts-expect-error "1" is invalid value for "overflow"
-      overflow: '1',
-      // @ts-expect-error "padding" is banned
+      overflowX: '1',
       padding: 0,
-      // @ts-expect-error "paddingLeft" cannot be numeric value
       paddingLeft: 5,
     },
   },
 });
 assertType({
+  // @ts-expect-error outline-box is an invalid value for box-sizing
   '.foo': {
-    // @ts-expect-error outline-box is an invalid value for box-sizing
     boxSizing: 'outline-box',
 
     '.bar': {
-      // @ts-expect-error outline-box is an invalid value for box-sizing
-      boxSizing: 'outline-box',
+      boxSizing: 'outline-box', // < no error here, TS only reports the error for the whole object
     },
   },
 });
 assertType({
+  // @ts-expect-error outline-box is an invalid value for box-sizing
   '.foo': {
-    // @ts-expect-error type check still fails on outline-box, not on any other line
     boxSizing: 'outline-box',
     zIndex: 1,
 
     '.bar': {
-      // @ts-expect-error type check still fails on outline-box, not on any other line
-      boxSizing: 'outline-box',
+      boxSizing: 'outline-box', // < no error here, TS only reports the error for the whole object
       zIndex: 1,
     },
   },
@@ -274,14 +312,38 @@ assertType({
   '.foo': {
     // @ts-expect-error Object is not assignable to CSS property
     zIndex: { color: 'red' },
-    // @ts-expect-error Object is not assignable to CSS property
-    opacity: { color: 'red' },
+    opacity: { color: 'red' }, // < no error here, TS only reports the first error in this case
 
     '.bar': {
-      // @ts-expect-error Object is not assignable to CSS property
-      zIndex: { color: 'red' },
-      // @ts-expect-error Object is not assignable to CSS property
-      opacity: { color: 'red' },
+      zIndex: { color: 'red' }, // < no error here, TS only reports the first error in this case
+      opacity: { color: 'red' }, // < no error here, TS only reports the first error in this case
     },
   },
 });
+
+// Fallback values
+assertType({
+  color: ['red', 'blue'],
+  paddingLeft: [0, '2px'],
+  zIndex: [10, 20],
+  ':hover': {
+    color: ['red', 'blue'],
+  },
+  ':hover:active': {
+    zIndex: [2],
+    color: ['red', 'blue'],
+    paddingLeft: [0, '2px'],
+  },
+});
+
+assertType({
+  paddingLeft: [
+    // @ts-expect-error "paddingLeft" cannot be numeric value
+    2,
+    '2px',
+  ],
+  // @ts-expect-error "paddingLeft" cannot be numeric value
+  ':hover:active': {
+    paddingLeft: ['2px', 2],
+  },
+});
diff --git a/packages/core/src/devtools/getDebugTree.test.ts b/packages/core/src/devtools/getDebugTree.test.ts
new file mode 100644
index 0000000..aebd6bf
--- /dev/null
+++ b/packages/core/src/devtools/getDebugTree.test.ts
@@ -0,0 +1,91 @@
+import { SEQUENCE_PREFIX } from '../constants';
+import { makeStyles } from '../makeStyles';
+import { mergeClasses } from '../mergeClasses';
+import { createDOMRenderer } from '../renderer/createDOMRenderer';
+import { MakeStylesOptions } from '../types';
+import { getDebugTree } from './getDebugTree';
+
+jest.mock('./isDevToolsEnabled', () => ({
+  isDevToolsEnabled: true,
+}));
+
+const options: MakeStylesOptions = {
+  dir: 'ltr',
+  renderer: createDOMRenderer(document),
+};
+
+const findSequenceHash = (classNames: string) =>
+  classNames.split(' ').find(className => className.startsWith(SEQUENCE_PREFIX));
+
+describe('getDebugTree', () => {
+  it.each<'ltr' | 'rtl'>(['ltr', 'rtl'])('returns styles merge tree when dir=%p', dir => {
+    const classes = makeStyles({
+      block: { display: 'block' },
+      grid: { display: 'grid' },
+    })({ ...options, dir });
+
+    const sequenceBlock = findSequenceHash(classes.block);
+    const sequenceGrid = findSequenceHash(classes.grid);
+
+    const className1 = mergeClasses('ui-button', classes.block);
+    const className2 = mergeClasses(className1, classes.grid);
+
+    const sequence1 = findSequenceHash(className1);
+    const sequence2 = findSequenceHash(className2);
+
+    expect(getDebugTree(sequence2!)).toEqual({
+      children: [
+        {
+          children: [
+            {
+              children: [],
+              debugClassNames: [
+                {
+                  className: 'ftgm304',
+                  overriddenBy: 'f13qh94s',
+                },
+              ],
+              direction: dir,
+              rules: {
+                ftgm304: '.ftgm304{display:block;}',
+              },
+              sequenceHash: sequenceBlock,
+              slot: 'block',
+            },
+          ],
+          debugClassNames: [
+            {
+              className: 'ftgm304',
+              overriddenBy: 'f13qh94s',
+            },
+          ],
+          direction: dir,
+          sequenceHash: sequence1,
+        },
+        {
+          children: [],
+          debugClassNames: [
+            {
+              className: 'f13qh94s',
+              overriddenBy: undefined,
+            },
+          ],
+          direction: dir,
+          rules: {
+            f13qh94s: '.f13qh94s{display:grid;}',
+          },
+          sequenceHash: sequenceGrid,
+          slot: 'grid',
+        },
+      ],
+      debugClassNames: [
+        {
+          className: 'f13qh94s',
+          overriddenBy: undefined,
+        },
+      ],
+      direction: dir,
+      sequenceHash: sequence2,
+    });
+  });
+});
diff --git a/packages/core/src/devtools/injectDevTools.test.ts b/packages/core/src/devtools/injectDevTools.test.ts
new file mode 100644
index 0000000..b178b7b
--- /dev/null
+++ b/packages/core/src/devtools/injectDevTools.test.ts
@@ -0,0 +1,19 @@
+import { injectDevTools } from './injectDevTools';
+
+describe('injectDevTools', () => {
+  it('adds __GRIFFEL_DEVTOOLS__ to document.defaultView', () => {
+    const document = { defaultView: {} };
+    injectDevTools(document as Document);
+    expect(document.defaultView).toHaveProperty('__GRIFFEL_DEVTOOLS__');
+
+    const descriptor = Object.getOwnPropertyDescriptor(document.defaultView, '__GRIFFEL_DEVTOOLS__');
+    expect(descriptor?.configurable).toBe(false);
+    expect(descriptor?.enumerable).toBe(false);
+  });
+
+  it('adds getInfo function to __GRIFFEL_DEVTOOLS__', () => {
+    const document = { defaultView: {} };
+    injectDevTools(document as Document);
+    expect((document.defaultView as Window)['__GRIFFEL_DEVTOOLS__']?.getInfo).toBeDefined();
+  });
+});
diff --git a/packages/core/src/devtools/store.test.ts b/packages/core/src/devtools/store.test.ts
new file mode 100644
index 0000000..8057fa7
--- /dev/null
+++ b/packages/core/src/devtools/store.test.ts
@@ -0,0 +1,66 @@
+import { SEQUENCE_PREFIX } from '../constants';
+import { makeStyles } from '../makeStyles';
+import { mergeClasses } from '../mergeClasses';
+import { createDOMRenderer } from '../renderer/createDOMRenderer';
+import { MakeStylesOptions } from '../types';
+import { debugData } from './store';
+
+jest.mock('./isDevToolsEnabled', () => ({
+  isDevToolsEnabled: true,
+}));
+
+const options: MakeStylesOptions = {
+  dir: 'ltr',
+  renderer: createDOMRenderer(document),
+};
+
+const findSequenceHash = (classNames: string) =>
+  classNames.split(' ').find(className => className.startsWith(SEQUENCE_PREFIX));
+
+describe('debugData', () => {
+  it('getChildrenSequences returns children sequences for multi-level merge', () => {
+    const classes = makeStyles({
+      block: { display: 'block' },
+      grid: { display: 'grid' },
+    })(options);
+
+    const sequenceBlock = findSequenceHash(classes.block);
+    const sequenceGrid = findSequenceHash(classes.grid);
+    expect(debugData.getChildrenSequences(sequenceGrid!)).toEqual([]);
+
+    const className1 = mergeClasses('ui-button', classes.block);
+    const className2 = mergeClasses(className1, classes.grid);
+
+    const sequence1 = findSequenceHash(className1);
+    const sequence2 = findSequenceHash(className2);
+
+    expect(debugData.getChildrenSequences(sequence1!)).toEqual([sequenceBlock]);
+    expect(debugData.getChildrenSequences(sequence2!)).toEqual([sequence1, sequenceGrid]);
+  });
+
+  it('getCSSRules returns cssRules', () => {
+    makeStyles({
+      block: { display: 'block', marginLeft: '10px' },
+    })(options);
+
+    expect(debugData.getCSSRules()).toEqual([
+      '.ftgm304{display:block;}',
+      '.f13qh94s{display:grid;}',
+      '.f1oou7ox{margin-left:10px;}',
+      '.f1pxv85q{margin-right:10px;}',
+    ]);
+  });
+
+  it.each<'ltr' | 'rtl'>(['rtl', 'ltr'])('getSequenceDetails returns slotName when dir=%p', dir => {
+    const classes = makeStyles({
+      block: { display: 'block' },
+      grid: { display: 'grid' },
+    })({ ...options, dir });
+
+    const sequenceBlock = findSequenceHash(classes.block);
+    const sequenceGrid = findSequenceHash(classes.grid);
+
+    expect(debugData.getSequenceDetails(sequenceBlock!)).toEqual({ slotName: 'block' });
+    expect(debugData.getSequenceDetails(sequenceGrid!)).toEqual({ slotName: 'grid' });
+  });
+});
diff --git a/packages/core/src/devtools/utils.test.ts b/packages/core/src/devtools/utils.test.ts
new file mode 100644
index 0000000..06d08fc
--- /dev/null
+++ b/packages/core/src/devtools/utils.test.ts
@@ -0,0 +1,75 @@
+import { LookupItem } from '../types';
+import { getDebugClassNames } from './utils';
+
+describe('getDebugClassNames', () => {
+  it('handles no parent debug node', () => {
+    const lookupItem: LookupItem = [
+      {
+        De3pzq: 'f3xbvq9',
+      },
+      'ltr',
+    ];
+    expect(getDebugClassNames(lookupItem)).toEqual([
+      {
+        className: 'f3xbvq9',
+        overriddenBy: undefined,
+      },
+    ]);
+  });
+
+  it('handles parent debug node does not contain overriding style', () => {
+    const lookupItem: LookupItem = [
+      {
+        De3pzq: 'f3xbvq9',
+      },
+      'ltr',
+    ];
+    const parentlLookupItem: LookupItem = [...lookupItem];
+    const parentDebugClassNames = [{ className: 'f3xbvq9', overriddenBy: undefined }];
+    expect(getDebugClassNames(lookupItem, parentlLookupItem, parentDebugClassNames)).toEqual([
+      {
+        className: 'f3xbvq9',
+        overriddenBy: undefined,
+      },
+    ]);
+  });
+
+  it('handles parent debug node contains overriding style', () => {
+    const lookupItem: LookupItem = [
+      {
+        De3pzq: 'fdmssx0',
+      },
+      'ltr',
+    ];
+    const parentlLookupItem: LookupItem = [
+      {
+        De3pzq: 'f3xbvq9',
+      },
+      'ltr',
+    ];
+    const parentDebugClassNames = [{ className: 'f3xbvq9', overriddenBy: undefined }];
+    expect(getDebugClassNames(lookupItem, parentlLookupItem, parentDebugClassNames)).toEqual([
+      {
+        className: 'fdmssx0',
+        overriddenBy: 'f3xbvq9',
+      },
+    ]);
+  });
+
+  it('handles when higher level ancestor debug node contains overriding style', () => {
+    const lookupItem: LookupItem = [
+      {
+        De3pzq: 'fdmssx0',
+      },
+      'ltr',
+    ];
+    const parentlLookupItem: LookupItem = [...lookupItem];
+    const parentDebugClassNames = [{ className: 'fdmssx0', overriddenBy: 'f3xbvq9' }];
+    expect(getDebugClassNames(lookupItem, parentlLookupItem, parentDebugClassNames)).toEqual([
+      {
+        className: 'fdmssx0',
+        overriddenBy: 'f3xbvq9',
+      },
+    ]);
+  });
+});
diff --git a/packages/core/src/makeStaticStyles.test.ts b/packages/core/src/makeStaticStyles.test.ts
index b0752a4..55e872b 100644
--- a/packages/core/src/makeStaticStyles.test.ts
+++ b/packages/core/src/makeStaticStyles.test.ts
@@ -122,7 +122,7 @@ describe('makeStaticStyles', () => {
     });
 
     useStaticStyles({ renderer });
-    expect(useStyles({ dir: 'ltr', renderer }).root).toBe('___23yvam0 fy9yzz7 f4ybsrx');
+    expect(useStyles({ dir: 'ltr', renderer }).root).toBe('___23yvam0_0000000 fy9yzz7 f4ybsrx');
 
     expect(renderer).toMatchInlineSnapshot(`
       @font-face {
diff --git a/packages/core/src/makeStyles.test.ts b/packages/core/src/makeStyles.test.ts
index dc8ee4e..71eed6a 100644
--- a/packages/core/src/makeStyles.test.ts
+++ b/packages/core/src/makeStyles.test.ts
@@ -9,6 +9,7 @@ describe('makeStyles', () => {
   let renderer: GriffelRenderer;
 
   beforeEach(() => {
+    process.env.NODE_ENV = 'production';
     renderer = createDOMRenderer(document);
   });
 
@@ -227,4 +228,16 @@ describe('makeStyles', () => {
       }
     `);
   });
+
+  it.each<'test' | 'development'>(['test', 'development'])(
+    'in non-production mode, hashes include debug information',
+    env => {
+      process.env.NODE_ENV = env;
+      const computeClasses = makeStyles({
+        root: { color: 'red' },
+      });
+
+      expect(computeClasses({ dir: 'ltr', renderer }).root).toEqual('___afhpfp0_0000000 fe3e8s9');
+    },
+  );
 });
diff --git a/packages/core/src/mergeClasses.test.ts b/packages/core/src/mergeClasses.test.ts
index 0bf0453..876d29c 100644
--- a/packages/core/src/mergeClasses.test.ts
+++ b/packages/core/src/mergeClasses.test.ts
@@ -2,7 +2,26 @@ import { mergeClasses } from './mergeClasses';
 import { makeStyles } from './makeStyles';
 import { createDOMRenderer } from './renderer/createDOMRenderer';
 import { MakeStylesOptions } from './types';
-import { SEQUENCE_PREFIX } from './constants';
+import { SEQUENCE_PREFIX, SEQUENCE_SIZE } from './constants';
+
+function removeSequenceHash(classNames: string) {
+  const indexOfSequence = classNames.indexOf(SEQUENCE_PREFIX);
+  if (indexOfSequence === -1) {
+    return classNames;
+  }
+
+  return (classNames.substr(0, indexOfSequence - 1) + classNames.substr(indexOfSequence + SEQUENCE_SIZE)).trim();
+}
+
+const mergeClassesSequenceHashRemoval: jest.SnapshotSerializerPlugin = {
+  test(value) {
+    return typeof value === 'string';
+  },
+  print(_value) {
+    return removeSequenceHash(_value as string);
+  },
+};
+expect.addSnapshotSerializer(mergeClassesSequenceHashRemoval);
 
 const options: MakeStylesOptions = {
   dir: 'ltr',
@@ -43,13 +62,17 @@ describe('mergeClasses', () => {
 
     const resultClassName = makeStyles({ root: { display: 'grid', paddingLeft: '5px' } })(options).root;
 
-    expect(mergeClasses(classes.block, classes.flex, classes.grid, classes.padding)).toBe(resultClassName);
+    expect(mergeClasses(classes.block, classes.flex, classes.grid, classes.padding)).toMatchInlineSnapshot(
+      removeSequenceHash(resultClassName),
+    );
   });
 
   it('order of classes is not important', () => {
     const className = makeStyles({ root: { display: 'block' } })(options).root;
 
-    expect(mergeClasses('ui-button', className, 'ui-button-content')).toBe(`ui-button ui-button-content ${className}`);
+    expect(mergeClasses('ui-button', className, 'ui-button-content')).toMatchInlineSnapshot(
+      `ui-button ui-button-content ${removeSequenceHash(className)}`,
+    );
   });
 
   it('order of classes is not important for multilevel overrides', () => {
@@ -60,7 +83,9 @@ describe('mergeClasses', () => {
     );
     const className2 = makeStyles({ root: { display: 'grid' } })(options).root;
 
-    expect(mergeClasses(className1, className2)).toBe(`ui-button ui-button-content ${className2}`);
+    expect(mergeClasses(className1, className2)).toMatchInlineSnapshot(
+      `ui-button ui-button-content ${removeSequenceHash(className2)}`,
+    );
   });
 
   it('merges multi-level overrides properly', () => {
@@ -76,9 +101,9 @@ describe('mergeClasses', () => {
     const sequence2 = mergeClasses('ui-flex', className3, className4);
     const sequence3 = mergeClasses(sequence1, sequence2, className5);
 
-    expect(sequence1).toBe(`ui-button ${className2}`);
-    expect(sequence2).toBe('ui-flex ___nsiv7r0 f13qh94s f15vdbe4');
-    expect(sequence3).toBe('ui-button ui-flex ___ma4nwa0 f13qh94s f15vdbe4 f1rqyxcv');
+    expect(sequence1).toMatchInlineSnapshot(`ui-button ${removeSequenceHash(className2)}`);
+    expect(sequence2).toMatchInlineSnapshot('ui-flex f13qh94s f15vdbe4');
+    expect(sequence3).toMatchInlineSnapshot('ui-button ui-flex f13qh94s f15vdbe4 f1rqyxcv');
   });
 
   it('warns if an unregistered sequence was passed', () => {
@@ -86,8 +111,12 @@ describe('mergeClasses', () => {
     const error = jest.spyOn(console, 'error').mockImplementationOnce(() => {});
     const className = makeStyles({ root: { display: 'block' } })(options).root;
 
-    expect(mergeClasses(className, `${SEQUENCE_PREFIX}abcdefg oprsqrt`)).toBe(className);
-    expect(error).toHaveBeenCalledWith(expect.stringMatching(/passed string contains an identifier \(___abcdefg\)/));
+    expect(mergeClasses(className, `${SEQUENCE_PREFIX}abcdefg_abcdefg oprsqrt`)).toMatchInlineSnapshot(
+      removeSequenceHash(className),
+    );
+    expect(error).toHaveBeenCalledWith(
+      expect.stringMatching(/passed string contains an identifier \(___abcdefg_abcdefg\)/),
+    );
   });
 
   it('warns if strings are not properly merged', () => {
@@ -125,12 +154,16 @@ describe('mergeClasses', () => {
       const rtlClasses1 = computeClasses({ ...options, dir: 'rtl' });
       const rtlClasses2 = computeClasses({ ...options, dir: 'rtl' });
 
-      expect(mergeClasses(rtlClasses1.start, rtlClasses2.start)).toBe(rtlClasses1.start);
-      expect(mergeClasses(rtlClasses1.start, rtlClasses2.start)).toBe(rtlClasses2.start);
-
-      expect(mergeClasses(rtlClasses1.start, rtlClasses2.start, rtlClasses1.end, rtlClasses2.end)).toBe(
-        '___1soy3ld f93e62u fo2qazs',
+      expect(mergeClasses(rtlClasses1.start, rtlClasses2.start)).toMatchInlineSnapshot(
+        removeSequenceHash(rtlClasses1.start),
       );
+      expect(mergeClasses(rtlClasses1.start, rtlClasses2.start)).toMatchInlineSnapshot(
+        removeSequenceHash(rtlClasses2.start),
+      );
+
+      expect(
+        mergeClasses(rtlClasses1.start, rtlClasses2.start, rtlClasses1.end, rtlClasses2.end),
+      ).toMatchInlineSnapshot('f93e62u fo2qazs');
     });
 
     it('merges multi-level overrides properly', () => {
@@ -142,7 +175,7 @@ describe('mergeClasses', () => {
       const sequence1 = mergeClasses('ui-button', classes.block);
       const sequence2 = mergeClasses(sequence1, classes.grid);
 
-      expect(sequence2).toBe(`ui-button ${classes.grid}`);
+      expect(sequence2).toMatchInlineSnapshot(`ui-button ${removeSequenceHash(classes.grid)}`);
     });
 
     it('classes for different text directions should not collide', () => {
@@ -155,8 +188,35 @@ describe('mergeClasses', () => {
       const rtlClassName1 = computeClasses1({ ...options, dir: 'rtl' }).root;
       const rtlClassName2 = computeClasses2({ ...options, dir: 'rtl' }).root;
 
-      expect(mergeClasses(ltrClassName1, ltrClassName2)).toBe('___1t65jhk fe3e8s9 frdkuqy');
-      expect(mergeClasses(rtlClassName1, rtlClassName2)).toBe('___w1tqsn0 fe3e8s9 f81rol6');
+      expect(mergeClasses(ltrClassName1, ltrClassName2)).toMatchInlineSnapshot('fe3e8s9 frdkuqy');
+      expect(mergeClasses(rtlClassName1, rtlClassName2)).toMatchInlineSnapshot('fe3e8s9 f81rol6');
+    });
+  });
+});
+
+describe('merges classes and generates sequence hashes', () => {
+  it('development', () => {
+    jest.isolateModules(() => {
+      process.env.NODE_ENV = 'development';
+      // eslint-disable-next-line @typescript-eslint/no-var-requires
+      const { mergeClasses } = require('./mergeClasses');
+      // eslint-disable-next-line @typescript-eslint/no-var-requires
+      const { makeStyles } = require('./makeStyles');
+      const className1 = makeStyles({ root: { display: 'block' } })(options).root;
+      const className2 = makeStyles({ root: { display: 'flex' } })(options).root;
+      expect(mergeClasses(className1, className2)).toBe('___1gzszts_39qb7g0 f22iagw');
+    });
+  });
+  it('production', async () => {
+    jest.isolateModules(() => {
+      process.env.NODE_ENV = 'production';
+      // eslint-disable-next-line @typescript-eslint/no-var-requires
+      const { mergeClasses } = require('./mergeClasses');
+      // eslint-disable-next-line @typescript-eslint/no-var-requires
+      const { makeStyles } = require('./makeStyles');
+      const className1 = makeStyles({ root: { display: 'block' } })(options).root;
+      const className2 = makeStyles({ root: { display: 'flex' } })(options).root;
+      expect(mergeClasses(className1, className2)).toBe('___1gzszts f22iagw');
     });
   });
 });
diff --git a/packages/core/src/runtime/compileCSS.test.ts b/packages/core/src/runtime/compileCSS.test.ts
index 47a977b..ea4bc06 100644
--- a/packages/core/src/runtime/compileCSS.test.ts
+++ b/packages/core/src/runtime/compileCSS.test.ts
@@ -36,6 +36,20 @@ describe('compileCSS', () => {
     `);
   });
 
+  it('handles array of values', () => {
+    expect(
+      compileCSS({
+        ...defaultOptions,
+        property: 'color',
+        value: ['red', 'blue'],
+      }),
+    ).toMatchInlineSnapshot(`
+      Array [
+        ".foo{color:red;color:blue;}",
+      ]
+    `);
+  });
+
   it('handles at-rules', () => {
     expect(
       compileCSS({
@@ -102,6 +116,23 @@ describe('compileCSS', () => {
     `);
   });
 
+  it('handles rtl properties with fallback values', () => {
+    expect(
+      compileCSS({
+        ...defaultOptions,
+        property: 'paddingLeft',
+        value: [0, '10px'],
+        rtlProperty: 'paddingRight',
+        rtlValue: [0, '10px'],
+      }),
+    ).toMatchInlineSnapshot(`
+      Array [
+        ".foo{padding-left:0;padding-left:10px;}",
+        ".rtl-foo{padding-right:0;padding-right:10px;}",
+      ]
+    `);
+  });
+
   describe('global', () => {
     it('compiles global rules', () => {
       expect(
diff --git a/packages/core/src/runtime/resolveStyleRules.test.ts b/packages/core/src/runtime/resolveStyleRules.test.ts
index baa1b1d..97938ba 100644
--- a/packages/core/src/runtime/resolveStyleRules.test.ts
+++ b/packages/core/src/runtime/resolveStyleRules.test.ts
@@ -165,6 +165,30 @@ describe('resolveStyleRules', () => {
       `);
     });
 
+    it('handles fallback values', () => {
+      const actual = resolveStyleRules({ color: ['red', 'blue'] });
+      expect(actual).toMatchInlineSnapshot(`
+        .f15e90lz {
+          color: red;
+          color: blue;
+        }
+      `);
+    });
+
+    it('handles empty array of fallback values', () => {
+      // eslint-disable-next-line @typescript-eslint/no-empty-function
+      const warn = jest.spyOn(console, 'warn').mockImplementationOnce(() => {});
+
+      const actual = resolveStyleRules({ color: [] });
+      expect(actual).toMatchInlineSnapshot(``); /* empty result */
+
+      expect(warn).toHaveBeenCalledWith(
+        expect.stringMatching(
+          /makeStyles\(\): An empty array was passed as input to "color", the property will be omitted in the styles./,
+        ),
+      );
+    });
+
     it('handles RTL', () => {
       expect(resolveStyleRules({ left: '5px' })).toMatchInlineSnapshot(`
         .f5b3q4t {
@@ -195,6 +219,54 @@ describe('resolveStyleRules', () => {
       expect(classnamesSet.size).toBe(2);
     });
 
+    it('handles fallback values in RTL', () => {
+      expect(
+        resolveStyleRules({
+          left: ['5px', '10px'],
+          float: ['initial', 'left'],
+        }),
+      ).toMatchInlineSnapshot(`
+        .f14hk0f5 {
+          left: 5px;
+          left: 10px;
+        }
+        .f18hwu1w {
+          right: 5px;
+          right: 10px;
+        }
+        .f8ngpof {
+          float: initial;
+          float: left;
+        }
+        .fhsnlhg {
+          float: initial;
+          float: right;
+        }
+      `);
+    });
+
+    it('errors if fallback values result in multiple properties in RTL, skips the property', () => {
+      // eslint-disable-next-line @typescript-eslint/no-empty-function
+      const error = jest.spyOn(console, 'error').mockImplementationOnce(() => {});
+
+      expect(
+        resolveStyleRules({
+          left: ['5px /* @noflip */', '10px'],
+          color: 'red',
+        }),
+      ).toMatchInlineSnapshot(`
+        .fe3e8s9 {
+          color: red;
+        }
+      `); /* only color */
+
+      expect(error).toHaveBeenCalledWith(
+        expect.stringMatching(
+          /mixing CSS fallback values which result in multiple CSS properties in RTL is not supported/,
+        ),
+      );
+    });
+
     it('handles nested selectors', () => {
       expect(resolveStyleRules({ ':hover': { color: 'red' } })).toMatchInlineSnapshot(`
         .faf35ka:hover {
diff --git a/packages/core/src/shorthands/border.test.ts b/packages/core/src/shorthands/border.test.ts
index ac916d7..c667124 100644
--- a/packages/core/src/shorthands/border.test.ts
+++ b/packages/core/src/shorthands/border.test.ts
@@ -39,4 +39,21 @@ describe('border', () => {
       borderTopColor: 'red',
     });
   });
+
+  it('for fallback value arrays', () => {
+    expect(border(['2px', '4px'], ['solid', 'dashed'], ['red', 'green'])).toEqual({
+      borderBottomColor: ['red', 'green'],
+      borderBottomStyle: ['solid', 'dashed'],
+      borderBottomWidth: ['2px', '4px'],
+      borderLeftColor: ['red', 'green'],
+      borderLeftStyle: ['solid', 'dashed'],
+      borderLeftWidth: ['2px', '4px'],
+      borderRightColor: ['red', 'green'],
+      borderRightStyle: ['solid', 'dashed'],
+      borderRightWidth: ['2px', '4px'],
+      borderTopColor: ['red', 'green'],
+      borderTopStyle: ['solid', 'dashed'],
+      borderTopWidth: ['2px', '4px'],
+    });
+  });
 });
diff --git a/packages/core/src/shorthands/borderBottom.test.ts b/packages/core/src/shorthands/borderBottom.test.ts
index 8844c55..1d8a94f 100644
--- a/packages/core/src/shorthands/borderBottom.test.ts
+++ b/packages/core/src/shorthands/borderBottom.test.ts
@@ -21,4 +21,12 @@ describe('borderBottom', () => {
       borderBottomColor: 'red',
     });
   });
+
+  it('for fallback value arrays', () => {
+    expect(borderBottom(['2px', '4px'], ['solid', 'dashed'], ['red', 'green'])).toEqual({
+      borderBottomColor: ['red', 'green'],
+      borderBottomStyle: ['solid', 'dashed'],
+      borderBottomWidth: ['2px', '4px'],
+    });
+  });
 });
diff --git a/packages/core/src/shorthands/borderColor.test.ts b/packages/core/src/shorthands/borderColor.test.ts
index c701d42..3371722 100644
--- a/packages/core/src/shorthands/borderColor.test.ts
+++ b/packages/core/src/shorthands/borderColor.test.ts
@@ -36,4 +36,13 @@ describe('borderColor', () => {
       borderTopColor: 'red',
     });
   });
+
+  it('for fallback value arrays', () => {
+    expect(borderColor(['red', '#f00'], ['blue', '#00f'], ['green', '#0f0'], ['yellow', '#ff0'])).toEqual({
+      borderBottomColor: ['green', '#0f0'],
+      borderLeftColor: ['yellow', '#ff0'],
+      borderRightColor: ['blue', '#00f'],
+      borderTopColor: ['red', '#f00'],
+    });
+  });
 });
diff --git a/packages/core/src/shorthands/borderLeft.test.ts b/packages/core/src/shorthands/borderLeft.test.ts
index 005ea24..b92a8d5 100644
--- a/packages/core/src/shorthands/borderLeft.test.ts
+++ b/packages/core/src/shorthands/borderLeft.test.ts
@@ -21,4 +21,12 @@ describe('borderLeft', () => {
       borderLeftColor: 'red',
     });
   });
+
+  it('for fallback value arrays', () => {
+    expect(borderLeft(['2px', '4px'], ['solid', 'dashed'], ['red', 'green'])).toEqual({
+      borderLeftColor: ['red', 'green'],
+      borderLeftStyle: ['solid', 'dashed'],
+      borderLeftWidth: ['2px', '4px'],
+    });
+  });
 });
diff --git a/packages/core/src/shorthands/borderRadius.test.ts b/packages/core/src/shorthands/borderRadius.test.ts
index fc6b486..63e331f 100644
--- a/packages/core/src/shorthands/borderRadius.test.ts
+++ b/packages/core/src/shorthands/borderRadius.test.ts
@@ -36,4 +36,13 @@ describe('borderRadius', () => {
       borderBottomLeftRadius: '4px',
     });
   });
+
+  it('for fallback value arrays', () => {
+    expect(borderRadius(['1px', '10px'], [0, '5px'], ['3px', '30px'], ['4px', 0])).toEqual({
+      borderBottomLeftRadius: ['4px', 0],
+      borderBottomRightRadius: ['3px', '30px'],
+      borderTopLeftRadius: ['1px', '10px'],
+      borderTopRightRadius: [0, '5px'],
+    });
+  });
 });
diff --git a/packages/core/src/shorthands/borderRight.test.ts b/packages/core/src/shorthands/borderRight.test.ts
index effc0a7..8c77afb 100644
--- a/packages/core/src/shorthands/borderRight.test.ts
+++ b/packages/core/src/shorthands/borderRight.test.ts
@@ -21,4 +21,12 @@ describe('borderRight', () => {
       borderRightColor: 'red',
     });
   });
+
+  it('for fallback value arrays', () => {
+    expect(borderRight(['2px', '4px'], ['solid', 'dashed'], ['red', 'green'])).toEqual({
+      borderRightColor: ['red', 'green'],
+      borderRightStyle: ['solid', 'dashed'],
+      borderRightWidth: ['2px', '4px'],
+    });
+  });
 });
diff --git a/packages/core/src/shorthands/borderStyle.test.ts b/packages/core/src/shorthands/borderStyle.test.ts
index 5f82fbc..525a62a 100644
--- a/packages/core/src/shorthands/borderStyle.test.ts
+++ b/packages/core/src/shorthands/borderStyle.test.ts
@@ -36,4 +36,13 @@ describe('borderStyle', () => {
       borderTopStyle: 'solid',
     });
   });
+
+  it('for fallback value arrays', () => {
+    expect(borderStyle(['solid', 'groove'], ['dashed', 'hidden'], ['dotted', 'none'], ['double', 'outset'])).toEqual({
+      borderBottomStyle: ['dotted', 'none'],
+      borderLeftStyle: ['double', 'outset'],
+      borderRightStyle: ['dashed', 'hidden'],
+      borderTopStyle: ['solid', 'groove'],
+    });
+  });
 });
diff --git a/packages/core/src/shorthands/borderTop.test.ts b/packages/core/src/shorthands/borderTop.test.ts
index 7e31fc5..dacf830 100644
--- a/packages/core/src/shorthands/borderTop.test.ts
+++ b/packages/core/src/shorthands/borderTop.test.ts
@@ -21,4 +21,12 @@ describe('borderTop', () => {
       borderTopColor: 'red',
     });
   });
+
+  it('for fallback value arrays', () => {
+    expect(borderTop(['2px', '4px'], ['solid', 'dashed'], ['red', 'green'])).toEqual({
+      borderTopColor: ['red', 'green'],
+      borderTopStyle: ['solid', 'dashed'],
+      borderTopWidth: ['2px', '4px'],
+    });
+  });
 });
diff --git a/packages/core/src/shorthands/borderWidth.test.ts b/packages/core/src/shorthands/borderWidth.test.ts
index cf8bfb2..68ab0df 100644
--- a/packages/core/src/shorthands/borderWidth.test.ts
+++ b/packages/core/src/shorthands/borderWidth.test.ts
@@ -45,4 +45,13 @@ describe('borderWidth', () => {
       borderTopWidth: 0,
     });
   });
+
+  it('for fallback value arrays', () => {
+    expect(borderWidth(['12px', 'initial'], ['24px', 0], ['36px', 'thin'], ['48px', '2em'])).toEqual({
+      borderBottomWidth: ['36px', 'thin'],
+      borderLeftWidth: ['48px', '2em'],
+      borderRightWidth: ['24px', 0],
+      borderTopWidth: ['12px', 'initial'],
+    });
+  });
 });
diff --git a/packages/core/src/shorthands/gap.test.ts b/packages/core/src/shorthands/gap.test.ts
index b99e4b2..2023b25 100644
--- a/packages/core/src/shorthands/gap.test.ts
+++ b/packages/core/src/shorthands/gap.test.ts
@@ -14,4 +14,11 @@ describe('gap', () => {
       rowGap: '24px',
     });
   });
+
+  it('for fallback value arrays', () => {
+    expect(gap(['12px', 0], ['normal', 'var(--customGap)'])).toEqual({
+      columnGap: ['12px', 0],
+      rowGap: ['normal', 'var(--customGap)'],
+    });
+  });
 });
diff --git a/packages/core/src/shorthands/generateStyles.ts b/packages/core/src/shorthands/generateStyles.ts
index 53a9999..4ae124d 100644
--- a/packages/core/src/shorthands/generateStyles.ts
+++ b/packages/core/src/shorthands/generateStyles.ts
@@ -1,13 +1,14 @@
-import { GriffelStylesCSSValue } from '../types';
+import type { GriffelStylesCSSValue, ValueOrArray } from '../types';
+import { GriffelStylesStrictCSSObject } from '../types';
 
 type DirectionalProperties = 'border' | 'padding' | 'margin';
 
 const positionMap = ['Top', 'Right', 'Bottom', 'Left'];
 
-export function generateStyles<Styles extends Record<string, GriffelStylesCSSValue>>(
+export function generateStyles<Styles extends GriffelStylesStrictCSSObject>(
   property: DirectionalProperties,
   suffix: '' | 'Color' | 'Style' | 'Width',
-  ...values: GriffelStylesCSSValue[]
+  ...values: ValueOrArray<GriffelStylesCSSValue>[]
 ): Styles {
   const [firstValue, secondValue = firstValue, thirdValue = firstValue, fourthValue = secondValue] = values;
   const valuesWithDefaults = [firstValue, secondValue, thirdValue, fourthValue];
@@ -18,7 +19,7 @@ export function generateStyles<Styles extends Record<string, GriffelStylesCSSVal
     if (valuesWithDefaults[i] || valuesWithDefaults[i] === 0) {
       const newKey = (property + positionMap[i] + suffix) as keyof Styles;
 
-      styles[newKey] = valuesWithDefaults[i] as Styles[keyof Styles];
+      styles[newKey] = valuesWithDefaults[i] as unknown as Styles[keyof Styles];
     }
   }
 
diff --git a/packages/core/src/shorthands/margin.test.ts b/packages/core/src/shorthands/margin.test.ts
index 281e326..cb954cd 100644
--- a/packages/core/src/shorthands/margin.test.ts
+++ b/packages/core/src/shorthands/margin.test.ts
@@ -45,4 +45,13 @@ describe('margin', () => {
       marginTop: 0,
     });
   });
+
+  it('for fallback value arrays', () => {
+    expect(margin([0, '12px'], ['auto', '-2px'])).toEqual({
+      marginBottom: [0, '12px'],
+      marginLeft: ['auto', '-2px'],
+      marginRight: ['auto', '-2px'],
+      marginTop: [0, '12px'],
+    });
+  });
 });
diff --git a/packages/core/src/shorthands/overflow.test.ts b/packages/core/src/shorthands/overflow.test.ts
index c21582b..29feece 100644
--- a/packages/core/src/shorthands/overflow.test.ts
+++ b/packages/core/src/shorthands/overflow.test.ts
@@ -14,4 +14,11 @@ describe('overflow', () => {
       overflowY: 'hidden',
     });
   });
+
+  it('for fallback value arrays', () => {
+    expect(overflow('visible', ['hidden', 'scroll'])).toEqual({
+      overflowX: 'visible',
+      overflowY: ['hidden', 'scroll'],
+    });
+  });
 });
diff --git a/packages/core/src/shorthands/padding.test.ts b/packages/core/src/shorthands/padding.test.ts
index b0496ba..76f7f9c 100644
--- a/packages/core/src/shorthands/padding.test.ts
+++ b/packages/core/src/shorthands/padding.test.ts
@@ -45,4 +45,13 @@ describe('padding', () => {
       paddingTop: 0,
     });
   });
+
+  it('for fallback value arrays', () => {
+    expect(padding([0, '12px'], ['2em', '2px'], 'auto')).toEqual({
+      paddingBottom: 'auto',
+      paddingLeft: ['2em', '2px'],
+      paddingRight: ['2em', '2px'],
+      paddingTop: [0, '12px'],
+    });
+  });
 });