Repository: microsoft/griffel (ts)

Issue:
bug(core): Typescript error using animationName inside of a nested selector
repro: https://codesandbox.io/s/elegant-moon-odwjen?file=/src/App.tsx

When using `animationName` inside a nested selector, there are unexpected typescript errors:

```
(property) "&[data-foo]": {
    animationName: {
        from: {
            opacity: number;
        };
        to: {
            opacity: number;
        };
    }[];
}
Type '{ animationName: { from: { opacity: number; }; to: { opacity: number; }; }[]; }' is not assignable to type 'string | number | (string | number)[] | GriffelStylesCSSObjectCustomL2'.
  Type '{ animationName: { from: { opacity: number; }; to: { opacity: number; }; }[]; }' is not assignable to type 'string'.ts(2322)
```

Pull request:
fix: introduce modern typings for TS >4
Fixes #378.

This PR introduces modern typings for TypeScript 4.1 and higher as this version supports recursive types references. This allow to simplify typings and remove weird workarounds. Also newer typings make TS errors more precise (check changes in the fixtures) 👍 

To keep compatibility with older versions of TS older typings are kept as legacy. To resolve them `typesVersions` field is used.

Patch:
diff --git a/change/@griffel-style-types-88cd8fa1-371b-4c2e-bb29-0d88568c620d.json b/change/@griffel-style-types-88cd8fa1-371b-4c2e-bb29-0d88568c620d.json
new file mode 100644
index 0000000..79c65ca
--- /dev/null
+++ b/change/@griffel-style-types-88cd8fa1-371b-4c2e-bb29-0d88568c620d.json
@@ -0,0 +1,7 @@
+{
+  "type": "patch",
+  "comment": "fix: resolve recursive typings issue",
+  "packageName": "@griffel/style-types",
+  "email": "olfedias@microsoft.com",
+  "dependentChangeType": "patch"
+}
diff --git a/packages/style-types/package.json b/packages/style-types/package.json
index 961d2fb..a65ab99 100644
--- a/packages/style-types/package.json
+++ b/packages/style-types/package.json
@@ -10,5 +10,12 @@
   "sideEffects": false,
   "dependencies": {
     "csstype": "^3.1.2"
+  },
+  "typesVersions": {
+    "<4.1": {
+      "src/index.d.ts": [
+        "src/legacy/index.d.ts"
+      ]
+    }
   }
 }
diff --git a/packages/style-types/src/legacy/README.md b/packages/style-types/src/legacy/README.md
new file mode 100644
index 0000000..90e1a44
--- /dev/null
+++ b/packages/style-types/src/legacy/README.md
@@ -0,0 +1,1 @@
+This folder contains types for legacy version of TypeScript (TS <4).
diff --git a/packages/style-types/src/legacy/index.ts b/packages/style-types/src/legacy/index.ts
new file mode 100644
index 0000000..bd6bcd7
--- /dev/null
+++ b/packages/style-types/src/legacy/index.ts
@@ -0,0 +1,6 @@
+export type { GriffelAnimation, GriffelStyle } from './makeStyles';
+export type { GriffelResetStyle } from './makeResetStyles';
+export type { GriffelStaticStyle, GriffelStaticStyles } from './makeStaticStyles';
+
+export type { GriffelStylesCSSValue, ValueOrArray } from '../shared';
+export type { GriffelStylesUnsupportedCSSProperties } from '../unsupported-properties';
diff --git a/packages/style-types/src/legacy/makeResetStyles.ts b/packages/style-types/src/legacy/makeResetStyles.ts
new file mode 100644
index 0000000..4804435
--- /dev/null
+++ b/packages/style-types/src/legacy/makeResetStyles.ts
@@ -0,0 +1,51 @@
+import * as CSS from 'csstype';
+import type { GriffelStylesCSSValue, ValueOrArray } from '../shared';
+
+//
+// Types for makeResetStyles()
+// ---
+//
+
+type GriffelResetStylesCSSProperties = Omit<
+  CSS.PropertiesFallback<ValueOrArray<GriffelStylesCSSValue>>,
+  // We have custom definition for "animationName"
+  'animationName'
+>;
+
+type GriffelResetStylesStrictCSSObject = GriffelResetStylesCSSProperties &
+  GriffelResetStylesCSSPseudos & {
+    animationName?: GriffelResetAnimation | GriffelResetAnimation[] | CSS.Property.Animation;
+  };
+
+type GriffelResetStylesCSSPseudos = {
+  [Property in CSS.Pseudos]?:
+    | (GriffelResetStylesStrictCSSObject & { content?: string | string[] })
+    | (GriffelResetStylesCSSObjectCustomL1 & { content?: string | string[] });
+};
+
+//
+// "GriffelStylesCSSObjectCustom*" is a workaround to avoid circular references in types that are breaking TS <4.
+//
+
+type GriffelResetStylesCSSObjectCustomL1 = {
+  [Property: string]: string | number | (string | number)[] | undefined | GriffelResetStylesCSSObjectCustomL2;
+} & GriffelResetStylesStrictCSSObject;
+
+type GriffelResetStylesCSSObjectCustomL2 = {
+  [Property: string]: string | number | (string | number)[] | undefined | GriffelResetStylesCSSObjectCustomL3;
+} & GriffelResetStylesStrictCSSObject;
+
+type GriffelResetStylesCSSObjectCustomL3 = {
+  [Property: string]: string | number | (string | number)[] | undefined | GriffelResetStylesCSSObjectCustomL4;
+} & GriffelResetStylesStrictCSSObject;
+
+type GriffelResetStylesCSSObjectCustomL4 = {
+  [Property: string]: string | number | (string | number)[] | undefined | GriffelResetStylesCSSObjectCustomL5;
+} & GriffelResetStylesStrictCSSObject;
+
+type GriffelResetStylesCSSObjectCustomL5 = {
+  [Property: string]: string | number | (string | number)[] | undefined | GriffelResetStylesStrictCSSObject;
+} & GriffelResetStylesStrictCSSObject;
+
+export type GriffelResetStyle = GriffelResetStylesStrictCSSObject | GriffelResetStylesCSSObjectCustomL1;
+export type GriffelResetAnimation = Record<'from' | 'to' | string, GriffelResetStyle>;
diff --git a/packages/style-types/src/legacy/makeStaticStyles.ts b/packages/style-types/src/legacy/makeStaticStyles.ts
new file mode 100644
index 0000000..50aa87c
--- /dev/null
+++ b/packages/style-types/src/legacy/makeStaticStyles.ts
@@ -0,0 +1,27 @@
+import * as CSS from 'csstype';
+
+//
+// Types for makeStaticStyles()
+// ---
+//
+
+export type GriffelStaticStyle = {
+  [key: string]: CSS.Properties &
+    // TODO Questionable: how else would users target their own children?
+    Record<string, any>; // eslint-disable-line @typescript-eslint/no-explicit-any
+} & {
+  '@font-face'?: {
+    fontFamily: string;
+    src: string;
+
+    fontFeatureSettings?: string;
+    fontStretch?: string;
+    fontStyle?: string;
+    fontVariant?: string;
+    fontVariationSettings?: string;
+    fontWeight?: number | string;
+
+    unicodeRange?: string;
+  };
+};
+export type GriffelStaticStyles = GriffelStaticStyle | string;
diff --git a/packages/style-types/src/legacy/makeStyles.ts b/packages/style-types/src/legacy/makeStyles.ts
new file mode 100644
index 0000000..b13ca42
--- /dev/null
+++ b/packages/style-types/src/legacy/makeStyles.ts
@@ -0,0 +1,54 @@
+import * as CSS from 'csstype';
+
+import type { GriffelStylesCSSValue } from '../shared';
+import type { GriffelStylesUnsupportedCSSProperties } from '../unsupported-properties';
+
+//
+// Types for makeStyles()
+// ---
+//
+
+type GriffelStylesCSSProperties = Omit<
+  CSS.PropertiesFallback<GriffelStylesCSSValue>,
+  // We have custom definition for "animationName"
+  'animationName'
+> &
+  Partial<GriffelStylesUnsupportedCSSProperties>;
+
+export type GriffelStylesStrictCSSObject = GriffelStylesCSSProperties &
+  GriffelStylesCSSPseudos & {
+    animationName?: GriffelAnimation | GriffelAnimation[] | CSS.Property.Animation;
+  };
+
+type GriffelStylesCSSPseudos = {
+  [Property in CSS.Pseudos]?:
+    | (GriffelStylesStrictCSSObject & { content?: string | string[] })
+    | (GriffelStylesCSSObjectCustomL1 & { content?: string | string[] });
+};
+
+//
+// "GriffelStylesCSSObjectCustom*" is a workaround to avoid circular references in types that are breaking TS <4.
+//
+
+type GriffelStylesCSSObjectCustomL1 = {
+  [Property: string]: string | number | (string | number)[] | undefined | GriffelStylesCSSObjectCustomL2;
+} & GriffelStylesStrictCSSObject;
+
+type GriffelStylesCSSObjectCustomL2 = {
+  [Property: string]: string | number | (string | number)[] | undefined | GriffelStylesCSSObjectCustomL3;
+} & GriffelStylesStrictCSSObject;
+
+type GriffelStylesCSSObjectCustomL3 = {
+  [Property: string]: string | number | (string | number)[] | undefined | GriffelStylesCSSObjectCustomL4;
+} & GriffelStylesStrictCSSObject;
+
+type GriffelStylesCSSObjectCustomL4 = {
+  [Property: string]: string | number | (string | number)[] | undefined | GriffelStylesCSSObjectCustomL5;
+} & GriffelStylesStrictCSSObject;
+
+type GriffelStylesCSSObjectCustomL5 = {
+  [Property: string]: string | number | (string | number)[] | undefined | GriffelStylesStrictCSSObject;
+} & GriffelStylesStrictCSSObject;
+
+export type GriffelStyle = GriffelStylesStrictCSSObject | GriffelStylesCSSObjectCustomL1;
+export type GriffelAnimation = Record<'from' | 'to' | string, GriffelStylesCSSObjectCustomL1>;
diff --git a/packages/style-types/src/makeResetStyles.ts b/packages/style-types/src/makeResetStyles.ts
index 0ccf7db..b084d64 100644
--- a/packages/style-types/src/makeResetStyles.ts
+++ b/packages/style-types/src/makeResetStyles.ts
@@ -1,5 +1,5 @@
 import * as CSS from 'csstype';
-import type { GriffelStylesCSSValue, ValueOrArray } from './shared';
+import type { GriffelStylesCSSValue } from './shared';
 
 //
 // Types for makeResetStyles()
@@ -7,45 +7,21 @@ import type { GriffelStylesCSSValue, ValueOrArray } from './shared';
 //
 
 type GriffelResetStylesCSSProperties = Omit<
-  CSS.PropertiesFallback<ValueOrArray<GriffelStylesCSSValue>>,
+  CSS.PropertiesFallback<GriffelStylesCSSValue>,
   // We have custom definition for "animationName"
   'animationName'
 >;
 
-type GriffelResetStylesStrictCSSObject = GriffelResetStylesCSSProperties &
-  GriffelResetStylesCSSPseudos & {
-    animationName?: GriffelResetAnimation | GriffelResetAnimation[] | CSS.Property.Animation;
-  };
+export type GriffelResetStylesStrictCSSObject = GriffelResetStylesCSSProperties &
+  GriffelCSSPseudos & { animationName?: GriffelResetAnimation | GriffelResetAnimation[] | string };
 
-type GriffelResetStylesCSSPseudos = {
-  [Property in CSS.Pseudos]?:
-    | (GriffelResetStylesStrictCSSObject & { content?: string | string[] })
-    | (GriffelResetStylesCSSObjectCustomL1 & { content?: string | string[] });
-};
-
-//
-// "GriffelStylesCSSObjectCustom*" is a workaround to avoid circular references in types that are breaking TS <4.
-//
-
-type GriffelResetStylesCSSObjectCustomL1 = {
-  [Property: string]: string | number | (string | number)[] | undefined | GriffelResetStylesCSSObjectCustomL2;
-} & GriffelResetStylesStrictCSSObject;
-
-type GriffelResetStylesCSSObjectCustomL2 = {
-  [Property: string]: string | number | (string | number)[] | undefined | GriffelResetStylesCSSObjectCustomL3;
+type GriffelCSSObjectCustom = {
+  [Property: string]: GriffelResetStyle | GriffelStylesCSSValue;
 } & GriffelResetStylesStrictCSSObject;
 
-type GriffelResetStylesCSSObjectCustomL3 = {
-  [Property: string]: string | number | (string | number)[] | undefined | GriffelResetStylesCSSObjectCustomL4;
-} & GriffelResetStylesStrictCSSObject;
-
-type GriffelResetStylesCSSObjectCustomL4 = {
-  [Property: string]: string | number | (string | number)[] | undefined | GriffelResetStylesCSSObjectCustomL5;
-} & GriffelResetStylesStrictCSSObject;
-
-type GriffelResetStylesCSSObjectCustomL5 = {
-  [Property: string]: string | number | (string | number)[] | undefined | GriffelResetStylesStrictCSSObject;
-} & GriffelResetStylesStrictCSSObject;
+type GriffelCSSPseudos = {
+  [Property in CSS.Pseudos]?: GriffelResetStylesStrictCSSObject | GriffelCSSObjectCustom;
+};
 
-export type GriffelResetStyle = GriffelResetStylesStrictCSSObject | GriffelResetStylesCSSObjectCustomL1;
-export type GriffelResetAnimation = Record<'from' | 'to' | string, GriffelResetStyle>;
+export type GriffelResetAnimation = Record<'from' | 'to' | string, GriffelCSSObjectCustom>;
+export type GriffelResetStyle = GriffelResetStylesStrictCSSObject | GriffelCSSObjectCustom;
diff --git a/packages/style-types/src/makeStyles.ts b/packages/style-types/src/makeStyles.ts
index ff2c842..9fb53a5 100644
--- a/packages/style-types/src/makeStyles.ts
+++ b/packages/style-types/src/makeStyles.ts
@@ -12,43 +12,17 @@ type GriffelStylesCSSProperties = Omit<
   CSS.PropertiesFallback<GriffelStylesCSSValue>,
   // We have custom definition for "animationName"
   'animationName'
-> &
-  Partial<GriffelStylesUnsupportedCSSProperties>;
+> & { animationName?: GriffelAnimation | GriffelAnimation[] | string } & Partial<GriffelStylesUnsupportedCSSProperties>;
 
-export type GriffelStylesStrictCSSObject = GriffelStylesCSSProperties &
-  GriffelStylesCSSPseudos & {
-    animationName?: GriffelAnimation | GriffelAnimation[] | CSS.Property.Animation;
-  };
+export type GriffelStylesStrictCSSObject = GriffelStylesCSSProperties & GriffelCSSPseudos;
 
-type GriffelStylesCSSPseudos = {
-  [Property in CSS.Pseudos]?:
-    | (GriffelStylesStrictCSSObject & { content?: string | string[] })
-    | (GriffelStylesCSSObjectCustomL1 & { content?: string | string[] });
-};
-
-//
-// "GriffelStylesCSSObjectCustom*" is a workaround to avoid circular references in types that are breaking TS <4.
-//
-
-type GriffelStylesCSSObjectCustomL1 = {
-  [Property: string]: string | number | (string | number)[] | undefined | GriffelStylesCSSObjectCustomL2;
-} & GriffelStylesStrictCSSObject;
-
-type GriffelStylesCSSObjectCustomL2 = {
-  [Property: string]: string | number | (string | number)[] | undefined | GriffelStylesCSSObjectCustomL3;
+type GriffelCSSObjectCustom = {
+  [Property: string]: GriffelStyle | GriffelStylesCSSValue;
 } & GriffelStylesStrictCSSObject;
 
-type GriffelStylesCSSObjectCustomL3 = {
-  [Property: string]: string | number | (string | number)[] | undefined | GriffelStylesCSSObjectCustomL4;
-} & GriffelStylesStrictCSSObject;
-
-type GriffelStylesCSSObjectCustomL4 = {
-  [Property: string]: string | number | (string | number)[] | undefined | GriffelStylesCSSObjectCustomL5;
-} & GriffelStylesStrictCSSObject;
-
-type GriffelStylesCSSObjectCustomL5 = {
-  [Property: string]: string | number | (string | number)[] | undefined | GriffelStylesStrictCSSObject;
-} & GriffelStylesStrictCSSObject;
+type GriffelCSSPseudos = {
+  [Property in CSS.Pseudos]?: GriffelStylesStrictCSSObject | GriffelCSSObjectCustom;
+};
 
-export type GriffelStyle = GriffelStylesStrictCSSObject | GriffelStylesCSSObjectCustomL1;
-export type GriffelAnimation = Record<'from' | 'to' | string, GriffelStylesCSSObjectCustomL1>;
+export type GriffelAnimation = Record<'from' | 'to' | string, GriffelCSSObjectCustom>;
+export type GriffelStyle = GriffelStylesStrictCSSObject | GriffelCSSObjectCustom;


Test patch:
diff --git a/e2e/typescript/src/assets/fixture-reset.ts b/e2e/typescript/src/assets/fixture-reset.ts
index ceb2cbe..51749f5 100644
--- a/e2e/typescript/src/assets/fixture-reset.ts
+++ b/e2e/typescript/src/assets/fixture-reset.ts
@@ -65,6 +65,13 @@ assertType({
   ':hover': { '--color': 'red' },
 });
 
+assertType({
+  ':hover': { content: "'foo'" },
+});
+assertType({
+  ':hover': { animationName: { from: {}, to: {} } },
+});
+
 // Custom selectors
 //
 
@@ -91,6 +98,9 @@ assertType({
 assertType({
   '.bar': { '--color': 'red' },
 });
+assertType({
+  '.bar': { animationName: { from: {}, to: {} } },
+});
 
 // Nested custom selectors
 //
@@ -189,6 +199,24 @@ assertType({
   '@media screen and (max-width: 992px)': {
     ...typedMixin,
   },
+  '@media(forced-colors: active)': {
+    ...typedMixin,
+    color: 'var(--customColor)',
+  },
+  ':after': {
+    ...typedMixin,
+  },
+  ':before': {
+    ...typedMixin,
+    color: 'var(--customColor)',
+  },
+  '& .foo': {
+    ...typedMixin,
+  },
+  '& .bar': {
+    ...typedMixin,
+    color: 'var(--customColor)',
+  },
 });
 
 // Strict selectors
@@ -214,24 +242,25 @@ assertType({
 //
 
 assertType({
-  // @ts-expect-error "1" is invalid value for "overflowX"
   ':hover:focus': {
+    // @ts-expect-error "1" is invalid value for "overflowX"
     overflowX: '1',
   },
 });
 
 assertType({
-  // @ts-expect-error "paddingLeft" cannot be numeric value
   ':hover:focus': {
+    // @ts-expect-error "paddingLeft" cannot be numeric value
     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',
+    // @ts-expect-error "1" is invalid value for "overflowX"
+    overflowX: '1',
     padding: 0,
+    // @ts-expect-error paddingLeft cannot be a numeric value
     paddingLeft: 5,
   },
 });
@@ -240,12 +269,14 @@ 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
+    // @ts-expect-error Object is not assignable to CSS property
+    opacity: { color: 'red' },
   },
   ':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
+    // @ts-expect-error Object is not assignable to CSS property
+    zIndex: { color: 'red' },
   },
 });
 
@@ -253,33 +284,36 @@ 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 "overflowX"
       overflowX: '1',
       padding: 0,
+      // @ts-expect-error paddingLeft cannot be a 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', // < 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 outline-box is an invalid value for box-sizing
     boxSizing: 'outline-box',
     zIndex: 1,
 
     '.bar': {
-      boxSizing: 'outline-box', // < no error here, TS only reports the error for the whole object
+      // @ts-expect-error outline-box is an invalid value for box-sizing
+      boxSizing: 'outline-box',
       zIndex: 1,
     },
   },
@@ -288,11 +322,14 @@ assertType({
   '.foo': {
     // @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
+    // @ts-expect-error Object is not assignable to CSS property
+    opacity: { color: 'red' },
 
     '.bar': {
-      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
+      // @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' },
     },
   },
 });
@@ -319,8 +356,11 @@ assertType({
     2,
     '2px',
   ],
-  // @ts-expect-error "paddingLeft" cannot be numeric value
   ':hover:active': {
-    paddingLeft: ['2px', 2],
+    paddingLeft: [
+      '2px',
+      // @ts-expect-error "paddingLeft" cannot be numeric value
+      2,
+    ],
   },
 });
diff --git a/e2e/typescript/src/assets/fixture.ts b/e2e/typescript/src/assets/fixture.ts
index c27c25d..7e6a74a 100644
--- a/e2e/typescript/src/assets/fixture.ts
+++ b/e2e/typescript/src/assets/fixture.ts
@@ -62,6 +62,13 @@ assertType({
   ':hover': { '--color': 'red' },
 });
 
+assertType({
+  ':hover': { content: "'foo'" },
+});
+assertType({
+  ':hover': { animationName: { from: {}, to: {} } },
+});
+
 // Custom selectors
 //
 
@@ -87,6 +94,9 @@ assertType({
 assertType({
   '.bar': { '--color': 'red' },
 });
+assertType({
+  '.bar': { animationName: { from: {}, to: {} } },
+});
 
 // Nested custom selectors
 //
@@ -192,7 +202,6 @@ 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)',
@@ -204,6 +213,13 @@ assertType({
     ...typedMixin,
     color: 'var(--customColor)',
   },
+  '& .foo': {
+    ...typedMixin,
+  },
+  '& .bar': {
+    ...typedMixin,
+    color: 'var(--customColor)',
+  },
 });
 
 // Strict selectors
@@ -231,31 +247,32 @@ assertType({
 //
 
 assertType({
-  // @ts-expect-error "1" is invalid value for "overflowX"
   ':hover:focus': {
+    // @ts-expect-error "1" is invalid value for "overflowX"
     overflowX: '1',
   },
 });
 
 assertType({
-  // @ts-expect-error "padding" is banned
   ':hover:focus': {
+    // @ts-expect-error "padding" is banned
     padding: 0,
   },
 });
 
 assertType({
-  // @ts-expect-error "paddingLeft" cannot be numeric value
   ':hover:focus': {
+    // @ts-expect-error "5" is an invalid value for paddingLeft
     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',
+    // @ts-expect-error "padding" is banned
     padding: 0,
+    // @ts-expect-error "5" is an invalid value for paddingLeft
     paddingLeft: 5,
   },
 });
@@ -264,12 +281,14 @@ 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
+    // @ts-expect-error Object is not assignable to CSS property
+    opacity: { color: 'red' },
   },
   ':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
+    // @ts-expect-error Object is not assignable to CSS property
+    zIndex: { color: 'red' },
   },
 });
 
@@ -277,33 +296,37 @@ 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 outline-box is an invalid value for box-sizing
       overflowX: '1',
+      // @ts-expect-error "padding" is banned
       padding: 0,
+      // @ts-expect-error "5" is an invalid value for paddingLeft
       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': {
-      boxSizing: 'outline-box', // < no error here, TS only reports the error for the whole object
+      // @ts-expect-error outline-box is an invalid value for box-sizing
+      boxSizing: 'outline-box',
     },
   },
 });
 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',
     zIndex: 1,
 
     '.bar': {
-      boxSizing: 'outline-box', // < no error here, TS only reports the error for the whole object
+      // @ts-expect-error outline-box is an invalid value for box-sizing
+      boxSizing: 'outline-box',
       zIndex: 1,
     },
   },
@@ -312,11 +335,14 @@ assertType({
   '.foo': {
     // @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
+    // @ts-expect-error Object is not assignable to CSS property
+    opacity: { color: 'red' },
 
     '.bar': {
-      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
+      // @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' },
     },
   },
 });
@@ -342,8 +368,11 @@ assertType({
     2,
     '2px',
   ],
-  // @ts-expect-error "paddingLeft" cannot be numeric value
   ':hover:active': {
-    paddingLeft: ['2px', 2],
+    paddingLeft: [
+      '2px',
+      // @ts-expect-error "paddingLeft" cannot be numeric value
+      2,
+    ],
   },
 });
diff --git a/e2e/typescript/src/assets/legacy/fixture-reset.ts b/e2e/typescript/src/assets/legacy/fixture-reset.ts
new file mode 100644
index 0000000..ceb2cbe
--- /dev/null
+++ b/e2e/typescript/src/assets/legacy/fixture-reset.ts
@@ -0,0 +1,326 @@
+import type { GriffelResetStyle } from '@griffel/style-types';
+
+function assertType(style: GriffelResetStyle): GriffelResetStyle {
+  return style;
+}
+
+// Animation
+assertType({ animationName: 'foo' });
+
+// Basic styles
+//
+
+assertType({ flex: 0 });
+assertType({ flex: 1 });
+assertType({ zIndex: 0 });
+assertType({ zIndex: 1 });
+
+assertType({ padding: '5px' });
+assertType({ paddingLeft: '5px' });
+assertType({ color: 'beige' });
+
+// CSS variables
+//
+
+assertType({ fontWeight: 'var(--foo)' });
+assertType({ flexShrink: 'var(--bar)' });
+assertType({ opacity: 'var(--baz)' });
+assertType({ zIndex: 'var(--qux)' });
+
+assertType({ '--color': 'red' });
+
+// Mixins
+//
+
+assertType({
+  ...{ padding: '5px' },
+  ...{ paddingLeft: '5px' },
+  ...{ color: 'red' },
+});
+
+// Strict selector defined via "CSS.Pseudos"
+//
+
+assertType({
+  ':hover': { flexShrink: 0 },
+  ':focus': { flexShrink: 'initial' },
+  ':active': { zIndex: 0 },
+  ':visited': { zIndex: 1 },
+});
+
+assertType({
+  ':hover': { fontWeight: 'var(--foo)' },
+  ':focus': { flexShrink: 'var(--bar)' },
+  ':active': { opacity: 'var(--bar)' },
+  ':visited': { zIndex: 'var(--qux)' },
+});
+
+assertType({
+  ':hover': { color: 'beige' },
+  ':active': { padding: '5px' },
+  ':focus': { paddingLeft: '5px' },
+});
+
+assertType({
+  ':hover': { '--color': 'red' },
+});
+
+// Custom selectors
+//
+
+assertType({
+  ':hover:focus': { flexShrink: 0 },
+  ':hover:active': { flexShrink: 'initial' },
+  ':hover:visited': { zIndex: 0 },
+  ':hover:focus-visible': { zIndex: 1 },
+});
+
+assertType({
+  ':link:hover': { fontWeight: 'var(--foo)' },
+  ':link:focus': { flexShrink: 'var(--bar)' },
+  ':link:active': { opacity: 'var(--bar)' },
+  ':link:visited': { zIndex: 'var(--qux)' },
+});
+
+assertType({
+  '.bar': { color: 'beige' },
+  '.foo': { padding: '5px' },
+  '.qux': { paddingLeft: '5px' },
+});
+
+assertType({
+  '.bar': { '--color': 'red' },
+});
+
+// Nested custom selectors
+//
+
+assertType({
+  '.foo': {
+    '.bar': { flexShrink: 0 },
+    '.baz': { flexShrink: 'initial' },
+    '.qux': { opacity: 0 },
+    '.fred': { zIndex: 0 },
+    '.thud': { zIndex: 1 },
+  },
+  '.bar': {
+    '.baz': { color: 'beige' },
+    '.qux': { paddingLeft: '5px' },
+  },
+  '.baz': {
+    '.qux': {
+      '--color': 'red',
+    },
+  },
+  '.qux': {
+    '.bar': { flexShrink: 'var(--bar)' },
+    '.baz': { opacity: 'var(--baz)' },
+  },
+});
+
+// Invalid values
+//
+
+assertType({
+  // @ts-expect-error "outline-box" is an invalid value for "box-sizing"
+  boxSizing: 'outline-box',
+});
+assertType({
+  // @ts-expect-error "1" is invalid value for "overflowX"
+  overflowX: '1',
+});
+assertType({
+  // @ts-expect-error "paddingLeft" cannot be numeric value
+  paddingLeft: 5,
+});
+assertType({
+  // @ts-expect-error "0" is invalid value for "color"
+  color: 0,
+});
+
+assertType({
+  // @ts-expect-error type check still fails on outline-box, not on any other line
+  boxSizing: 'outline-box',
+  zIndex: 1,
+});
+assertType({
+  zIndex: 1,
+  // @ts-expect-error type check still fails on outline-box, not on any other line
+  boxSizing: 'outline-box',
+});
+
+assertType({
+  // @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' },
+});
+
+// Mixins with invalid values
+//
+
+// @ts-expect-error Object in "zIndex" is not assignable to CSS property
+assertType({
+  ...{ zIndex: { color: 'red' } },
+  ...{ color: 'red' },
+});
+// @ts-expect-error "outline-box" in "boxSizing" is an invalid value for "box-sizing"
+assertType({
+  ...{ boxSizing: 'outline-box' },
+  ...{ color: 'red' },
+});
+
+// Just a type check, deep objects are not expected to be used as style mixins?
+const typedMixin: GriffelResetStyle = {
+  marginLeft: '5px',
+  ':hover': {
+    marginLeft: '6px',
+    '--customColor': 'blue',
+  },
+  '--customColor': 'silver',
+};
+
+assertType({
+  ...typedMixin,
+  color: 'var(--customColor)',
+});
+
+assertType({
+  '@media screen and (max-width: 992px)': {
+    ...typedMixin,
+  },
+});
+
+// Strict selectors
+//
+
+assertType({
+  ':hover': {
+    // @ts-expect-error "1" is invalid value for "overflowX"
+    overflowX: '1',
+    // @ts-expect-error "paddingLeft" cannot be numeric value
+    paddingLeft: 5,
+  },
+});
+assertType({
+  ':hover': {
+    // @ts-expect-error outline-box is an invalid value for box-sizing
+    boxSizing: 'outline-box',
+    zIndex: 1,
+  },
+});
+
+// Custom selectors
+//
+
+assertType({
+  // @ts-expect-error "1" is invalid value for "overflowX"
+  ':hover:focus': {
+    overflowX: '1',
+  },
+});
+
+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
+  },
+});
+
+// Nested custom selectors
+//
+
+assertType({
+  // @ts-expect-error "1" is invalid value for "overflowX", padding is banned, paddingLeft cannot be a numeric value
+  '.foo': {
+    '.baz': {
+      overflowX: '1',
+      padding: 0,
+      paddingLeft: 5,
+    },
+  },
+});
+assertType({
+  // @ts-expect-error outline-box is an invalid value for box-sizing
+  '.foo': {
+    boxSizing: 'outline-box',
+
+    '.bar': {
+      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': {
+    boxSizing: 'outline-box',
+    zIndex: 1,
+
+    '.bar': {
+      boxSizing: 'outline-box', // < no error here, TS only reports the error for the whole object
+      zIndex: 1,
+    },
+  },
+});
+assertType({
+  '.foo': {
+    // @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
+
+    '.bar': {
+      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'],
+  padding: [0, '2px'],
+  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/e2e/typescript/src/assets/legacy/fixture.ts b/e2e/typescript/src/assets/legacy/fixture.ts
new file mode 100644
index 0000000..c27c25d
--- /dev/null
+++ b/e2e/typescript/src/assets/legacy/fixture.ts
@@ -0,0 +1,349 @@
+import type { GriffelStyle } from '@griffel/style-types';
+
+function assertType(style: GriffelStyle): GriffelStyle {
+  return style;
+}
+
+// Animation
+assertType({ animationName: 'foo' });
+
+// Basic styles
+//
+
+assertType({ flexShrink: 0 });
+assertType({ flexShrink: 1 });
+assertType({ zIndex: 0 });
+assertType({ zIndex: 1 });
+
+assertType({ paddingLeft: '5px' });
+assertType({ color: 'beige' });
+
+// CSS variables
+//
+
+assertType({ fontWeight: 'var(--foo)' });
+assertType({ flexShrink: 'var(--bar)' });
+assertType({ opacity: 'var(--baz)' });
+assertType({ zIndex: 'var(--qux)' });
+
+assertType({ '--color': 'red' });
+
+// Mixins
+//
+
+assertType({
+  ...{ paddingLeft: '5px' },
+  ...{ color: 'red' },
+});
+
+// Strict selector defined via "CSS.Pseudos"
+//
+
+assertType({
+  ':hover': { flexShrink: 0 },
+  ':focus': { flexShrink: 'initial' },
+  ':active': { zIndex: 0 },
+  ':visited': { zIndex: 1 },
+});
+
+assertType({
+  ':hover': { fontWeight: 'var(--foo)' },
+  ':focus': { flexShrink: 'var(--bar)' },
+  ':active': { opacity: 'var(--bar)' },
+  ':visited': { zIndex: 'var(--qux)' },
+});
+
+assertType({
+  ':hover': { color: 'beige' },
+  ':focus': { paddingLeft: '5px' },
+});
+
+assertType({
+  ':hover': { '--color': 'red' },
+});
+
+// Custom selectors
+//
+
+assertType({
+  ':hover:focus': { flexShrink: 0 },
+  ':hover:active': { flexShrink: 'initial' },
+  ':hover:visited': { zIndex: 0 },
+  ':hover:focus-visible': { zIndex: 1 },
+});
+
+assertType({
+  ':link:hover': { fontWeight: 'var(--foo)' },
+  ':link:focus': { flexShrink: 'var(--bar)' },
+  ':link:active': { opacity: 'var(--bar)' },
+  ':link:visited': { zIndex: 'var(--qux)' },
+});
+
+assertType({
+  '.bar': { color: 'beige' },
+  '.foo': { paddingLeft: '5px' },
+});
+
+assertType({
+  '.bar': { '--color': 'red' },
+});
+
+// Nested custom selectors
+//
+
+assertType({
+  '.foo': {
+    '.bar': { flexShrink: 0 },
+    '.baz': { flexShrink: 'initial' },
+    '.qux': { opacity: 0 },
+    '.fred': { zIndex: 0 },
+    '.thud': { zIndex: 1 },
+  },
+  '.bar': {
+    '.baz': { color: 'beige' },
+    '.qux': { paddingLeft: '5px' },
+  },
+  '.baz': {
+    '.qux': {
+      '--color': 'red',
+    },
+  },
+  '.qux': {
+    '.bar': { flexShrink: 'var(--bar)' },
+    '.baz': { opacity: 'var(--baz)' },
+  },
+});
+
+// Banned shorthand properties
+//
+
+assertType({
+  // @ts-expect-error "padding" is banned
+  padding: 0,
+});
+assertType({
+  // @ts-expect-error "borderLeft" is banned
+  borderLeft: '5px',
+});
+
+// Invalid values
+//
+
+assertType({
+  // @ts-expect-error "outline-box" is an invalid value for "box-sizing"
+  boxSizing: 'outline-box',
+});
+assertType({
+  // @ts-expect-error "1" is invalid value for "overflowX"
+  overflowX: '1',
+});
+assertType({
+  // @ts-expect-error "paddingLeft" cannot be numeric value
+  paddingLeft: 5,
+});
+assertType({
+  // @ts-expect-error "0" is invalid value for "color"
+  color: 0,
+});
+
+assertType({
+  // @ts-expect-error type check still fails on outline-box, not on any other line
+  boxSizing: 'outline-box',
+  zIndex: 1,
+});
+
+assertType({
+  // @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' },
+});
+
+// Mixins with invalid values
+//
+
+// @ts-expect-error Object in "zIndex" is not assignable to CSS property
+assertType({
+  ...{ zIndex: { color: 'red' } },
+  ...{ color: 'red' },
+});
+// @ts-expect-error "outline-box" in "boxSizing" is an invalid value for "box-sizing"
+assertType({
+  ...{ boxSizing: 'outline-box' },
+  ...{ color: 'red' },
+});
+
+// Just a type check, deep objects are not expected to be used as style mixins?
+const typedMixin: GriffelStyle = {
+  marginLeft: '5px',
+  ':hover': {
+    marginLeft: '6px',
+    '--customColor': 'blue',
+  },
+  '--customColor': 'silver',
+};
+
+assertType({
+  ...typedMixin,
+  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 "overflowX"
+    overflowX: '1',
+    // @ts-expect-error "padding" is banned
+    padding: 0,
+    // @ts-expect-error "paddingLeft" cannot be numeric value
+    paddingLeft: 5,
+  },
+});
+assertType({
+  ':hover': {
+    // @ts-expect-error outline-box is an invalid value for box-sizing
+    boxSizing: 'outline-box',
+    zIndex: 1,
+  },
+});
+
+// Custom selectors
+//
+
+assertType({
+  // @ts-expect-error "1" is invalid value for "overflowX"
+  ':hover:focus': {
+    overflowX: '1',
+  },
+});
+
+assertType({
+  // @ts-expect-error "padding" is banned
+  ':hover:focus': {
+    padding: 0,
+  },
+});
+
+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
+  },
+});
+
+// Nested custom selectors
+//
+
+assertType({
+  // @ts-expect-error "1" is invalid value for "overflowX", padding is banned, paddingLeft cannot be a numeric value
+  '.foo': {
+    '.baz': {
+      overflowX: '1',
+      padding: 0,
+      paddingLeft: 5,
+    },
+  },
+});
+assertType({
+  // @ts-expect-error outline-box is an invalid value for box-sizing
+  '.foo': {
+    boxSizing: 'outline-box',
+
+    '.bar': {
+      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': {
+    boxSizing: 'outline-box',
+    zIndex: 1,
+
+    '.bar': {
+      boxSizing: 'outline-box', // < no error here, TS only reports the error for the whole object
+      zIndex: 1,
+    },
+  },
+});
+assertType({
+  '.foo': {
+    // @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
+
+    '.bar': {
+      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/e2e/typescript/src/assets/legacy/tsconfig.fixture.json b/e2e/typescript/src/assets/legacy/tsconfig.fixture.json
new file mode 100644
index 0000000..f8f07f8
--- /dev/null
+++ b/e2e/typescript/src/assets/legacy/tsconfig.fixture.json
@@ -0,0 +1,9 @@
+{
+  "compilerOptions": {
+    "lib": ["ES2019", "dom"],
+    "isolatedModules": true,
+    "moduleResolution": "node",
+    "types": []
+  },
+  "include": ["*.ts"]
+}
diff --git a/e2e/typescript/src/test.ts b/e2e/typescript/src/test.ts
index c17e785..b6e9d8d 100644
--- a/e2e/typescript/src/test.ts
+++ b/e2e/typescript/src/test.ts
@@ -2,17 +2,24 @@ import { configureYarn, copyAssets, createTempDir, installPackages, packLocalPac
 import * as logSymbols from 'log-symbols';
 import * as path from 'path';
 
-async function performTest(tsVersion: string) {
+async function performTest(tsVersion: string, options: { mode?: 'legacy' | 'modern' } = {}) {
   let tempDir: string;
   let tscBin: string;
 
+  const { mode = 'modern' } = options;
+
   try {
     const rootDir = path.resolve(__dirname, '..', '..', '..');
+    const assetsPath = path.resolve(__dirname, 'assets', mode === 'legacy' ? 'legacy' : '');
 
     tempDir = createTempDir('typescript');
 
+    if (mode === 'legacy') {
+      console.log(logSymbols.info, 'Using legacy mode...');
+    }
+
     await copyAssets({
-      assetsPath: path.resolve(__dirname, 'assets'),
+      assetsPath: assetsPath,
       tempDir,
       renames: { 'tsconfig.fixture.json': 'tsconfig.json' },
     });
@@ -43,7 +50,10 @@ async function performTest(tsVersion: string) {
   }
 
   try {
-    await sh(`node ${tscBin} --noEmit --pretty`, tempDir);
+    const command = `node ${tscBin} --noEmit --pretty`;
+
+    console.log(logSymbols.info, 'Running', command);
+    await sh(command, tempDir);
 
     console.log(logSymbols.success, `Example project was successfully built with typescript@${tsVersion}`);
     console.log('');
@@ -65,7 +75,7 @@ async function performTest(tsVersion: string) {
 }
 
 (async () => {
-  await performTest('3.9');
+  await performTest('3.9', { mode: 'legacy' });
   await performTest('4.1');
   await performTest('4.4');
   await performTest('4.9');
diff --git a/e2e/typescript/tsconfig.lib.json b/e2e/typescript/tsconfig.lib.json
index 0893e0a..f13bd07 100644
--- a/e2e/typescript/tsconfig.lib.json
+++ b/e2e/typescript/tsconfig.lib.json
@@ -4,5 +4,6 @@
     "outDir": "../../dist/out-tsc",
     "types": ["node", "environment"]
   },
-  "include": ["**/*.ts", "**/*.js"]
+  "include": ["**/*.ts", "**/*.js"],
+  "exclude": ["src/assets/legacy/*"]
 }
diff --git a/e2e/utils/src/createTempDir.ts b/e2e/utils/src/createTempDir.ts
index e5e6e8c..120ec2a 100644
--- a/e2e/utils/src/createTempDir.ts
+++ b/e2e/utils/src/createTempDir.ts
@@ -3,7 +3,7 @@ import * as logSymbols from 'log-symbols';
 
 export function createTempDir(prefix: string) {
   // "Unsafe" means delete even if it still has files inside (our desired behavior)
-  const tempDir = tmp.dirSync({ prefix: 'nextjs', unsafeCleanup: true }).name;
+  const tempDir = tmp.dirSync({ prefix, unsafeCleanup: true }).name;
   console.log(logSymbols.success, `Temporary directory created under ${tempDir}`);
 
   return tempDir;