Repository: microsoft/griffel (ts)

Issue:
@containe at rule used in makeStyles  "yanks" out the shared attribute
For a given style:

```
const useAppStyles = makeStyles({
  ....otherStyles,
  header: {
    backgroundColor: "pink",
    "@container bar (width > 400px)": {
      backgroundColor: "magenta",
      color: "orange"
    },
    "@media (min-width: 600px)": {
      backgroundColor: "green"
    }
  }
});}
```

The `backgroundColor` attribute is removed from the base style and the `@media` block when defined in the `@container` block.
Is this the expected behavior, or the `backgroundColor` should not be touched outside of the `@container` block?
If `backgroundColor` is removed from the `@container` block everything is working as expected.

Test project: https://codesandbox.io/s/griffel-container-forked-h7v2qw?file=/src/App.js


Pull request:
fix(core): add container queries to hash generation
Fixes #399. See https://github.com/microsoft/griffel/issues/399#issuecomment-1640927987 for explainer.

Patch:
diff --git a/change/@griffel-core-4aed8fac-fc17-499c-8ecc-37b384c5d500.json b/change/@griffel-core-4aed8fac-fc17-499c-8ecc-37b384c5d500.json
new file mode 100644
index 0000000..a32b8d0
--- /dev/null
+++ b/change/@griffel-core-4aed8fac-fc17-499c-8ecc-37b384c5d500.json
@@ -0,0 +1,7 @@
+{
+  "type": "patch",
+  "comment": "fix: add container queries to hash generation",
+  "packageName": "@griffel/core",
+  "email": "olfedias@microsoft.com",
+  "dependentChangeType": "patch"
+}
diff --git a/packages/core/src/makeStyles.ts b/packages/core/src/makeStyles.ts
index 136c800..256f38b 100644
--- a/packages/core/src/makeStyles.ts
+++ b/packages/core/src/makeStyles.ts
@@ -47,6 +47,7 @@ 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>);
diff --git a/packages/core/src/runtime/resolveStyleRules.ts b/packages/core/src/runtime/resolveStyleRules.ts
index 5d0559b..cb32aa7 100644
--- a/packages/core/src/runtime/resolveStyleRules.ts
+++ b/packages/core/src/runtime/resolveStyleRules.ts
@@ -93,8 +93,9 @@ export function resolveStyleRules(
 
     if (typeof value === 'string' || typeof value === 'number') {
       // uniq key based on a hash of property & selector, used for merging later
-      const key = hashPropertyKey(selectors, media, support, property);
+      const key = hashPropertyKey(selectors, container, media, support, property);
       const className = hashClassName({
+        container,
         media,
         layer,
         value: value.toString(),
@@ -108,6 +109,7 @@ export function resolveStyleRules(
 
       const rtlClassName = flippedInRtl
         ? hashClassName({
+            container,
             value: rtlDefinition.value.toString(),
             property: rtlDefinition.key,
             selectors,
@@ -200,8 +202,9 @@ export function resolveStyleRules(
         continue;
       }
 
-      const key = hashPropertyKey(selectors, media, support, property);
+      const key = hashPropertyKey(selectors, container, media, support, property);
       const className = hashClassName({
+        container,
         media,
         layer,
         value: value.map(v => (v ?? '').toString()).join(';'),
@@ -227,6 +230,7 @@ export function resolveStyleRules(
 
       const rtlClassName = flippedInRtl
         ? hashClassName({
+            container,
             value: rtlDefinitions.map(v => (v?.value ?? '').toString()).join(';'),
             property: rtlDefinitions[0].key,
             selectors,
@@ -315,6 +319,7 @@ export function resolveStyleRules(
         // The only way to target multiple containers is to nest container queries
         // https://developer.mozilla.org/en-US/docs/Web/CSS/@container#nested_container_queries
         const containerQuery = property.slice(10).trim();
+
         resolveStyleRules(
           value as GriffelStyle,
           selectors,
diff --git a/packages/core/src/runtime/utils/hashClassName.ts b/packages/core/src/runtime/utils/hashClassName.ts
index f7538f7..1866859 100644
--- a/packages/core/src/runtime/utils/hashClassName.ts
+++ b/packages/core/src/runtime/utils/hashClassName.ts
@@ -1,18 +1,27 @@
 import hashString from '@emotion/hash';
 import { HASH_PREFIX } from '../../constants';
 
-export interface HashedClassNameParts {
+interface HashedClassNameParts {
   property: string;
   value: string;
   selectors: string[];
   media: string;
   layer: string;
   support: string;
+  container: string;
 }
 
-export function hashClassName({ media, layer, property, selectors, support, value }: HashedClassNameParts): string {
+export function hashClassName({
+  container,
+  media,
+  layer,
+  property,
+  selectors,
+  support,
+  value,
+}: HashedClassNameParts): string {
   // Trimming of value is required to generate consistent hashes
-  const classNameHash = hashString(selectors.join('') + media + layer + support + property + value.trim());
+  const classNameHash = hashString(selectors.join('') + container + media + layer + support + property + value.trim());
 
   return HASH_PREFIX + classNameHash;
 }
diff --git a/packages/core/src/runtime/utils/hashPropertyKey.ts b/packages/core/src/runtime/utils/hashPropertyKey.ts
index f6e9ca8..22d60b5 100644
--- a/packages/core/src/runtime/utils/hashPropertyKey.ts
+++ b/packages/core/src/runtime/utils/hashPropertyKey.ts
@@ -1,9 +1,15 @@
 import hash from '@emotion/hash';
 import { PropertyHash } from '../../types';
 
-export function hashPropertyKey(selectors: string[], media: string, support: string, property: string): PropertyHash {
+export function hashPropertyKey(
+  selectors: string[],
+  container: string,
+  media: string,
+  support: string,
+  property: string,
+): PropertyHash {
   // uniq key based on property & selector, used for merging later
-  const computedKey = selectors.join('') + media + support + property;
+  const computedKey = selectors.join('') + container + media + support + property;
 
   // "key" can be really long as it includes selectors, we use hashes to reduce sizes of keys
   // ".foo :hover" => "abcd"


Test patch:
diff --git a/packages/core/src/runtime/resolveStyleRules.test.ts b/packages/core/src/runtime/resolveStyleRules.test.ts
index 258017a..64a35bd 100644
--- a/packages/core/src/runtime/resolveStyleRules.test.ts
+++ b/packages/core/src/runtime/resolveStyleRules.test.ts
@@ -434,28 +434,60 @@ describe('resolveStyleRules', () => {
 
     it('handles named container queries', () => {
       const result = resolveStyleRules({ '@container foo (max-width: 1px)': { color: 'red' } });
+
       expect(result).toMatchInlineSnapshot(`
         @container foo (max-width: 1px) {
-          .fe3e8s9 {
+          .f16pkaez {
             color: red;
           }
         }
       `);
-
-      expect(Object.keys(result[1])).toEqual(['c']);
+      expect(result[0]).toMatchInlineSnapshot(`
+        Object {
+          "Blqc43h": "f16pkaez",
+        }
+      `);
     });
 
     it('handles unnamed container queries', () => {
       const result = resolveStyleRules({ '@container (max-width: 1px)': { color: 'red' } });
+
       expect(result).toMatchInlineSnapshot(`
         @container (max-width: 1px) {
-          .fe3e8s9 {
+          .f15adh02 {
             color: red;
           }
         }
       `);
+      expect(result[0]).toMatchInlineSnapshot(`
+        Object {
+          "qc8hou": "f15adh02",
+        }
+      `);
+    });
 
-      expect(Object.keys(result[1])).toEqual(['c']);
+    it("container queries don't collide with other properties", () => {
+      const result = resolveStyleRules({
+        color: 'red',
+        '@container foo (max-width: 1px)': { color: 'red' },
+      });
+
+      expect(result).toMatchInlineSnapshot(`
+        .fe3e8s9 {
+          color: red;
+        }
+        @container foo (max-width: 1px) {
+          .f16pkaez {
+            color: red;
+          }
+        }
+      `);
+      expect(result[0]).toMatchInlineSnapshot(`
+        Object {
+          "Blqc43h": "f16pkaez",
+          "sj55zd": "fe3e8s9",
+        }
+      `);
     });
 
     it('handles media queries', () => {
diff --git a/packages/core/src/runtime/utils/hashPropertyKey.test.ts b/packages/core/src/runtime/utils/hashPropertyKey.test.ts
index 08a3bca..173b0d3 100644
--- a/packages/core/src/runtime/utils/hashPropertyKey.test.ts
+++ b/packages/core/src/runtime/utils/hashPropertyKey.test.ts
@@ -2,10 +2,10 @@ import { hashPropertyKey } from './hashPropertyKey';
 
 describe('hashPropertyKey', () => {
   it('generates hashes that always start with letters', () => {
-    expect(hashPropertyKey([''], '', '', 'color')).toBe('sj55zd');
-    expect(hashPropertyKey([''], '', '', 'display')).toBe('mc9l5x');
+    expect(hashPropertyKey([''], '', '', '', 'color')).toBe('sj55zd');
+    expect(hashPropertyKey([''], '', '', '', 'display')).toBe('mc9l5x');
 
-    expect(hashPropertyKey([''], '', '', 'backgroundColor')).toBe('De3pzq');
-    expect(hashPropertyKey([':hover'], '', '', 'color')).toBe('Bi91k9c');
+    expect(hashPropertyKey([''], '', '', '', 'backgroundColor')).toBe('De3pzq');
+    expect(hashPropertyKey([':hover'], '', '', '', 'color')).toBe('Bi91k9c');
   });
 });