Repository: microsoft/griffel (ts)

Issue:
core: shorthands for padding-inline/padding-block/margin-inline/margin-block
Hi, I'd like to ask to add shorthands and checks for padding-inline/padding-block/margin-inline/margin-block, which are padding and margin under the context of possible different writing modes. This would be essential for building inclusive UI.

reference: https://developer.mozilla.org/en-US/docs/Web/CSS/padding-inline

Pull request:
feat: add shorthands for (padding|margin)(Block|Inline) and bump csstype
Fixes #341.

- Adds shorthands for `paddingBlock`, `paddingInline`, `marginBlock`, `marginInline`
- Adds new properties to unsupported list

Patch:
diff --git a/change/@griffel-core-9d8d5494-38e5-4cc8-a08f-7331ea274f79.json b/change/@griffel-core-9d8d5494-38e5-4cc8-a08f-7331ea274f79.json
new file mode 100644
index 0000000..0ef830d
--- /dev/null
+++ b/change/@griffel-core-9d8d5494-38e5-4cc8-a08f-7331ea274f79.json
@@ -0,0 +1,7 @@
+{
+  "type": "minor",
+  "comment": "feat: add shorthands for (padding|margin)(Block|Inline) and bump csstype",
+  "packageName": "@griffel/core",
+  "email": "olfedias@microsoft.com",
+  "dependentChangeType": "patch"
+}
diff --git a/change/@griffel-eslint-plugin-02039725-378e-4e64-a568-33eec429ab32.json b/change/@griffel-eslint-plugin-02039725-378e-4e64-a568-33eec429ab32.json
new file mode 100644
index 0000000..77a709a
--- /dev/null
+++ b/change/@griffel-eslint-plugin-02039725-378e-4e64-a568-33eec429ab32.json
@@ -0,0 +1,7 @@
+{
+  "type": "patch",
+  "comment": "chore: bump csstype & add new properties to no-shorthands rule",
+  "packageName": "@griffel/eslint-plugin",
+  "email": "olfedias@microsoft.com",
+  "dependentChangeType": "patch"
+}
diff --git a/change/@griffel-eslint-plugin-a3b29662-7937-4c57-8d41-9ce578721b9f.json b/change/@griffel-eslint-plugin-a3b29662-7937-4c57-8d41-9ce578721b9f.json
new file mode 100644
index 0000000..6cf428d
--- /dev/null
+++ b/change/@griffel-eslint-plugin-a3b29662-7937-4c57-8d41-9ce578721b9f.json
@@ -0,0 +1,7 @@
+{
+  "type": "minor",
+  "comment": "Added a styles-file rule for enforcing makeStyles call be only made in files that end in .styles.ts",
+  "packageName": "@griffel/eslint-plugin",
+  "email": "1581488+christiango@users.noreply.github.com",
+  "dependentChangeType": "patch"
+}
diff --git a/package.json b/package.json
index f49411f..843eeea 100644
--- a/package.json
+++ b/package.json
@@ -148,7 +148,7 @@
     "@typescript-eslint/utils": "^5.47.0",
     "ajv": "^8.4.0",
     "browserslist": "^4.19.1",
-    "csstype": "^3.0.10",
+    "csstype": "^3.1.2",
     "enhanced-resolve": "^5.8.2",
     "rtl-css-js": "^1.16.1",
     "source-map-js": "1.0.2",
diff --git a/packages/core/package.json b/packages/core/package.json
index 056e86d..25cf99b 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -10,7 +10,7 @@
   "sideEffects": false,
   "dependencies": {
     "@emotion/hash": "^0.9.0",
-    "csstype": "^3.0.10",
+    "csstype": "^3.1.2",
     "rtl-css-js": "^1.16.1",
     "stylis": "^4.0.13",
     "tslib": "^2.1.0"
diff --git a/packages/core/src/constants.ts b/packages/core/src/constants.ts
index c598930..120580f 100644
--- a/packages/core/src/constants.ts
+++ b/packages/core/src/constants.ts
@@ -81,8 +81,11 @@ export const UNSUPPORTED_CSS_PROPERTIES: Record<keyof GriffelStylesUnsupportedCS
   borderStyle: 1,
   borderTop: 1,
   borderWidth: 1,
+  caret: 1,
   columns: 1,
   columnRule: 1,
+  containIntrinsicSize: 1,
+  container: 1,
   flex: 1,
   flexFlow: 1,
   font: 1,
@@ -92,9 +95,14 @@ export const UNSUPPORTED_CSS_PROPERTIES: Record<keyof GriffelStylesUnsupportedCS
   gridColumn: 1,
   gridRow: 1,
   gridTemplate: 1,
+  inset: 1,
+  insetBlock: 1,
+  insetInline: 1,
   lineClamp: 1,
   listStyle: 1,
   margin: 1,
+  marginBlock: 1,
+  marginInline: 1,
   mask: 1,
   maskBorder: 1,
   motion: 1,
@@ -103,8 +111,19 @@ export const UNSUPPORTED_CSS_PROPERTIES: Record<keyof GriffelStylesUnsupportedCS
   overflow: 1,
   overscrollBehavior: 1,
   padding: 1,
+  paddingBlock: 1,
+  paddingInline: 1,
   placeItems: 1,
+  placeContent: 1,
   placeSelf: 1,
+  scrollMargin: 1,
+  scrollMarginBlock: 1,
+  scrollMarginInline: 1,
+  scrollPadding: 1,
+  scrollPaddingBlock: 1,
+  scrollPaddingInline: 1,
+  scrollSnapMargin: 1,
+  scrollTimeline: 1,
   textDecoration: 1,
   textEmphasis: 1,
   transition: 1,
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index 10cdbc6..dbc071e 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -14,7 +14,11 @@ import {
   gap,
   gridArea,
   margin,
+  marginBlock,
+  marginInline,
   padding,
+  paddingBlock,
+  paddingInline,
   overflow,
   inset,
   outline,
@@ -35,7 +39,11 @@ export const shorthands = {
   gap,
   gridArea,
   margin,
+  marginBlock,
+  marginInline,
   padding,
+  paddingBlock,
+  paddingInline,
   overflow,
   inset,
   outline,
diff --git a/packages/core/src/shorthands/index.ts b/packages/core/src/shorthands/index.ts
index 1c21249..ffb6077 100644
--- a/packages/core/src/shorthands/index.ts
+++ b/packages/core/src/shorthands/index.ts
@@ -11,7 +11,11 @@ export { flex } from './flex';
 export { gap } from './gap';
 export { gridArea } from './gridArea';
 export { margin } from './margin';
+export { marginBlock } from './marginBlock';
+export { marginInline } from './marginInline';
 export { padding } from './padding';
+export { paddingBlock } from './paddingBlock';
+export { paddingInline } from './paddingInline';
 export { overflow } from './overflow';
 export { inset } from './inset';
 export { outline } from './outline';
diff --git a/packages/core/src/shorthands/marginBlock.ts b/packages/core/src/shorthands/marginBlock.ts
new file mode 100644
index 0000000..885dbcf
--- /dev/null
+++ b/packages/core/src/shorthands/marginBlock.ts
@@ -0,0 +1,20 @@
+import type { GriffelStylesStrictCSSObject } from '../types';
+import { MarginBlockInput } from './types';
+
+type MarginBlockStyle = Pick<GriffelStylesStrictCSSObject, 'marginBlockStart' | 'marginBlockEnd'>;
+
+/**
+ * A function that implements CSS spec conformant expansion for "margin-block"
+ *
+ * @example
+ *   marginBlock('10px')
+ *   marginBlock('10px', '5px')
+ *
+ * See https://developer.mozilla.org/en-US/docs/Web/CSS/margin-block
+ */
+export function marginBlock(start: MarginBlockInput, end: MarginBlockInput = start): MarginBlockStyle {
+  return {
+    marginBlockStart: start,
+    marginBlockEnd: end,
+  };
+}
diff --git a/packages/core/src/shorthands/marginInline.ts b/packages/core/src/shorthands/marginInline.ts
new file mode 100644
index 0000000..eeb24d5
--- /dev/null
+++ b/packages/core/src/shorthands/marginInline.ts
@@ -0,0 +1,20 @@
+import type { GriffelStylesStrictCSSObject } from '../types';
+import { MarginInlineInput } from './types';
+
+type MarginInlineStyle = Pick<GriffelStylesStrictCSSObject, 'marginInlineStart' | 'marginInlineEnd'>;
+
+/**
+ * A function that implements CSS spec conformant expansion for "margin-inline"
+ *
+ * @example
+ *   marginInline('10px')
+ *   marginInline('10px', '5px')
+ *
+ * See https://developer.mozilla.org/en-US/docs/Web/CSS/margin-inline
+ */
+export function marginInline(start: MarginInlineInput, end: MarginInlineInput = start): MarginInlineStyle {
+  return {
+    marginInlineStart: start,
+    marginInlineEnd: end,
+  };
+}
diff --git a/packages/core/src/shorthands/paddingBlock.ts b/packages/core/src/shorthands/paddingBlock.ts
new file mode 100644
index 0000000..9c8a134
--- /dev/null
+++ b/packages/core/src/shorthands/paddingBlock.ts
@@ -0,0 +1,20 @@
+import type { GriffelStylesStrictCSSObject } from '../types';
+import { PaddingBlockInput } from './types';
+
+type PaddingBlockStyle = Pick<GriffelStylesStrictCSSObject, 'paddingBlockStart' | 'paddingBlockEnd'>;
+
+/**
+ * A function that implements CSS spec conformant expansion for "padding-block"
+ *
+ * @example
+ *   paddingBlock('10px')
+ *   paddingBlock('10px', '5px')
+ *
+ * See https://developer.mozilla.org/en-US/docs/Web/CSS/padding-block
+ */
+export function paddingBlock(start: PaddingBlockInput, end: PaddingBlockInput = start): PaddingBlockStyle {
+  return {
+    paddingBlockStart: start,
+    paddingBlockEnd: end,
+  };
+}
diff --git a/packages/core/src/shorthands/paddingInline.ts b/packages/core/src/shorthands/paddingInline.ts
new file mode 100644
index 0000000..67623c0
--- /dev/null
+++ b/packages/core/src/shorthands/paddingInline.ts
@@ -0,0 +1,20 @@
+import type { GriffelStylesStrictCSSObject } from '../types';
+import { PaddingInlineInput } from './types';
+
+type PaddingInlineStyle = Pick<GriffelStylesStrictCSSObject, 'paddingInlineStart' | 'paddingInlineEnd'>;
+
+/**
+ * A function that implements CSS spec conformant expansion for "padding-inline"
+ *
+ * @example
+ *   paddingInline('10px')
+ *   paddingInline('10px', '5px')
+ *
+ * See https://developer.mozilla.org/en-US/docs/Web/CSS/padding-inline
+ */
+export function paddingInline(start: PaddingInlineInput, end: PaddingInlineInput = start): PaddingInlineStyle {
+  return {
+    paddingInlineStart: start,
+    paddingInlineEnd: end,
+  };
+}
diff --git a/packages/core/src/shorthands/types.ts b/packages/core/src/shorthands/types.ts
index 0d6dc05..341e891 100644
--- a/packages/core/src/shorthands/types.ts
+++ b/packages/core/src/shorthands/types.ts
@@ -16,10 +16,14 @@ export type GapInput = ValueOrArray<CSS.Property.Gap<GriffelStylesCSSValue>>;
 export type GridAreaInput = CSS.Property.GridArea;
 
 export type MarginInput = ValueOrArray<CSS.Property.Margin<GriffelStylesCSSValue>>;
+export type MarginBlockInput = ValueOrArray<CSS.Property.MarginBlock<GriffelStylesCSSValue>>;
+export type MarginInlineInput = ValueOrArray<CSS.Property.MarginInline<GriffelStylesCSSValue>>;
 
 export type OverflowInput = ValueOrArray<CSS.Property.Overflow>;
 
 export type PaddingInput = ValueOrArray<CSS.Property.Padding<GriffelStylesCSSValue>>;
+export type PaddingBlockInput = ValueOrArray<CSS.Property.MarginBlock<GriffelStylesCSSValue>>;
+export type PaddingInlineInput = ValueOrArray<CSS.Property.MarginInline<GriffelStylesCSSValue>>;
 
 export type InsetInput = ValueOrArray<CSS.Property.Inset<GriffelStylesCSSValue>>;
 
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 39fa928..32e6a08 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -48,10 +48,16 @@ export interface GriffelStylesUnsupportedCSSProperties extends Record<keyof CSS.
   borderTop: never;
   /** @deprecated Use `shorthands.borderWidth()` instead. */
   borderWidth: never;
+  /** @deprecated Use corresponding longhand properties `caretColor` and `caretShape` instead. */
+  caret: never;
   /** @deprecated Use corresponding longhand properties `columnCount` and `columnWidth` instead. */
   columns: never;
   /** @deprecated Use corresponding longhand properties such as `columnRuleWidth` and `columnRuleColor` instead. */
   columnRule: never;
+  /** @deprecated Use corresponding longhand properties `containIntrinsicWidth` and `containIntrinsicHeight` instead. */
+  containIntrinsicSize: never;
+  /** @deprecated Use corresponding longhand properties `containerName` and `containerType` instead. */
+  container: never;
   /** @deprecated Use `shorthands.flex()` instead. */
   flex: never;
   /** @deprecated Use corresponding longhand properties `flexWrap` and `flexDirection` instead. */
@@ -70,12 +76,22 @@ export interface GriffelStylesUnsupportedCSSProperties extends Record<keyof CSS.
   gridRow: never;
   /** @deprecated Use corresponding longhand properties such as `gridTemplateColumns` and `gridTemplateRows` instead. */
   gridTemplate: never;
+  /** @deprecated Use corresponding longhand properties `top`, `right`, `left` and `bottom` instead. */
+  inset: never;
+  /** @deprecated Use corresponding longhand properties such as `insetBlockStart` and `insetBlockEnd` instead. */
+  insetBlock: never;
+  /** @deprecated Use corresponding longhand properties such as `insetInlineStart` and `insetInlineEnd` instead. */
+  insetInline: never;
   /** @deprecated */
   lineClamp: never;
   /** @deprecated Use corresponding longhand properties such as `listStyleType` instead. */
   listStyle: never;
   /** @deprecated Use `shorthands.margin()` instead. */
   margin: never;
+  /** @deprecated Use `shorthands.marginBlock()` instead. */
+  marginBlock: never;
+  /** @deprecated Use `shorthands.marginInline()` instead. */
+  marginInline: never;
   /** @deprecated Use corresponding longhand properties such as `maskImage` and `maskSize` instead. */
   mask: never;
   /** @deprecated Use corresponding longhand properties such as `maskBorderSource` and `maskBorderWidth` instead. */
@@ -92,10 +108,32 @@ export interface GriffelStylesUnsupportedCSSProperties extends Record<keyof CSS.
   overscrollBehavior: never;
   /** @deprecated Use `shorthands.padding()` instead. */
   padding: never;
+  /** @deprecated Use `shorthands.paddingBlock()` instead. */
+  paddingBlock: never;
+  /** @deprecated Use `shorthands.paddingInline()` instead. */
+  paddingInline: never;
   /** @deprecated Use corresponding longhand properties `alignItems` and `justifyItems` instead. */
   placeItems: never;
+  /** @deprecated Use corresponding longhand properties `alignContent` and `justifyContent` instead. */
+  placeContent: never;
   /** @deprecated Use corresponding longhand properties `alignSelf` and `justifySelf` instead. */
   placeSelf: never;
+  /** @deprecated */
+  scrollMargin: never;
+  /** @deprecated */
+  scrollMarginBlock: never;
+  /** @deprecated */
+  scrollMarginInline: never;
+  /** @deprecated */
+  scrollPadding: never;
+  /** @deprecated */
+  scrollPaddingBlock: never;
+  /** @deprecated */
+  scrollPaddingInline: never;
+  /** @deprecated */
+  scrollSnapMargin: never;
+  /** @deprecated */
+  scrollTimeline: never;
   /** @deprecated Use corresponding longhand properties such as `textDecorationColor` and `textDecorationLine` instead. */
   textDecoration: never;
   /** @deprecated Use corresponding longhand properties `textEmphasisColor` and `textEmphasisStyle` instead. */
diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json
index 1efcebc..7ee5f6e 100644
--- a/packages/eslint-plugin/package.json
+++ b/packages/eslint-plugin/package.json
@@ -8,7 +8,7 @@
     "url": "https://github.com/microsoft/griffel"
   },
   "dependencies": {
-    "csstype": "^3.0.10",
+    "csstype": "^3.1.2",
     "@typescript-eslint/utils": "^5.47.0",
     "tslib": "^2.1.0"
   },
diff --git a/packages/eslint-plugin/src/index.ts b/packages/eslint-plugin/src/index.ts
index e1f8761..3606248 100644
--- a/packages/eslint-plugin/src/index.ts
+++ b/packages/eslint-plugin/src/index.ts
@@ -1,6 +1,7 @@
 import { recommendedConfig } from './configs/recommended';
 import { hookNamingRule } from './rules/hook-naming';
 import { noShorthandsRule } from './rules/no-shorthands';
+import { stylesFileRule } from './rules/styles-file';
 
 export = {
   configs: {
@@ -9,5 +10,6 @@ export = {
   rules: {
     'hook-naming': hookNamingRule,
     'no-shorthands': noShorthandsRule,
+    'styles-file': stylesFileRule,
   },
 };
diff --git a/packages/eslint-plugin/src/rules/no-shorthands.ts b/packages/eslint-plugin/src/rules/no-shorthands.ts
index 14b41fb..cd56041 100644
--- a/packages/eslint-plugin/src/rules/no-shorthands.ts
+++ b/packages/eslint-plugin/src/rules/no-shorthands.ts
@@ -29,8 +29,11 @@ const UNSUPPORTED_CSS_PROPERTIES: Record<keyof CSS.StandardShorthandProperties,
   borderStyle: true,
   borderTop: true,
   borderWidth: true,
+  caret: true,
   columns: true,
   columnRule: true,
+  containIntrinsicSize: true,
+  container: true,
   flex: true,
   flexFlow: true,
   font: true,
@@ -40,9 +43,14 @@ const UNSUPPORTED_CSS_PROPERTIES: Record<keyof CSS.StandardShorthandProperties,
   gridColumn: true,
   gridRow: true,
   gridTemplate: true,
+  inset: true,
+  insetBlock: true,
+  insetInline: true,
   lineClamp: true,
   listStyle: true,
   margin: true,
+  marginBlock: true,
+  marginInline: true,
   mask: true,
   maskBorder: true,
   motion: true,
@@ -51,8 +59,19 @@ const UNSUPPORTED_CSS_PROPERTIES: Record<keyof CSS.StandardShorthandProperties,
   overflow: true,
   overscrollBehavior: true,
   padding: true,
+  paddingBlock: true,
+  paddingInline: true,
   placeItems: true,
+  placeContent: true,
   placeSelf: true,
+  scrollMargin: true,
+  scrollMarginBlock: true,
+  scrollMarginInline: true,
+  scrollPadding: true,
+  scrollPaddingBlock: true,
+  scrollPaddingInline: true,
+  scrollSnapMargin: true,
+  scrollTimeline: true,
   textDecoration: true,
   textEmphasis: true,
   transition: true,
diff --git a/packages/eslint-plugin/src/rules/styles-file.md b/packages/eslint-plugin/src/rules/styles-file.md
new file mode 100644
index 0000000..1b11fa8
--- /dev/null
+++ b/packages/eslint-plugin/src/rules/styles-file.md
@@ -0,0 +1,33 @@
+# Enforce that `makeStyles()` calls are placed in a .styles.ts file (`@griffel/styles files`)
+
+Ensures that all `makeStyles()` and `makeResetStyles()` calls are placed in a `.styles.js` or `.styles.ts` file. The primary motivation for enabling rules is to reduce the overhead of Griffel CSS extraction build tools. With this rule enabled, your repo could be configured so the Griffel babel and webpack build tools only look at `.styles.ts` and `.styles.js` files rather than all .ts and .js files.
+
+## Rule Details
+
+`makeStyles()` calls should only exist inside `.styles.js` or `.styles.ts` files.
+
+Examples of **incorrect** code for this rule:
+
+```js
+// filename: component.tsx
+import { makeStyles } from '@griffel/react';
+
+export const useStyles = makeStyles({
+  root: {
+    backgroundColor: 'red',
+  },
+});
+```
+
+Examples of **correct** code for this rule:
+
+```js
+// filename: component.styles.ts
+import { makeStyles } from '@griffel/react';
+
+export const useStyles = makeStyles({
+  root: {
+    backgroundColor: 'red',
+  },
+});
+```
diff --git a/packages/eslint-plugin/src/rules/styles-file.ts b/packages/eslint-plugin/src/rules/styles-file.ts
new file mode 100644
index 0000000..38f2957
--- /dev/null
+++ b/packages/eslint-plugin/src/rules/styles-file.ts
@@ -0,0 +1,107 @@
+import { createRule } from '../utils/createRule';
+import { ESLintUtils, TSESTree } from '@typescript-eslint/utils';
+
+const MATCHING_PACKAGES = new Set(['@fluentui/react-components', '@griffel/core', '@griffel/react']);
+const STYLES_FILE_PATTERN = /^.*\.(styles)\.[j|t]s$/;
+
+function isMatchingPackage(packageName: string) {
+  return MATCHING_PACKAGES.has(packageName);
+}
+
+function isStylesFile(fileName: string) {
+  return STYLES_FILE_PATTERN.test(fileName);
+}
+
+/**
+ * @param node - import node from AST
+ * @returns true if makeStyles is imported, or if the entire library is imported. Otherwise returns false
+ */
+function isMakeStylesImport(node: TSESTree.ImportDeclaration) {
+  return (
+    isMatchingPackage(node.source.value) &&
+    node.specifiers.filter(specifier => {
+      // import * as ... from
+      if (specifier.type === 'ImportNamespaceSpecifier') {
+        return true;
+      }
+
+      if ('imported' in specifier) {
+        return (
+          specifier.imported.name === 'makeStyles' || // import { makeStyles } from
+          specifier.imported.name === 'makeResetStyles' // import { makeResetStyles } from
+        );
+      }
+
+      return false;
+    }).length > 0
+  );
+}
+
+/**
+ * @param callExpression CallExpression node in AST
+ * @returns if this is a makeStyles call or not
+ */
+function isMakeStyleCallExpression({ callee }: TSESTree.CallExpression) {
+  return (
+    ('name' in callee && callee.name === 'makeStyles') || // makeStyles({})
+    ('property' in callee && 'name' in callee.property && callee.property.name === 'makeStyles') // something.makeStyles({})
+  );
+}
+
+export const RULE_NAME = 'styles-file';
+
+export const stylesFileRule: ReturnType<ReturnType<typeof ESLintUtils.RuleCreator>> = createRule({
+  name: RULE_NAME,
+  meta: {
+    docs: {
+      description: 'Enforce that hooks returned from makeStyles() calls are placed in a .styles.ts file',
+      recommended: 'error',
+    },
+    messages: {
+      foundMakeStylesUsage:
+        'Styles created from `makeStyles` should be defined in a .styles.ts file so they can be extracted into static CSS',
+    },
+    type: 'problem',
+    schema: [],
+  },
+  defaultOptions: [],
+  create(context) {
+    const fileName = context.getFilename();
+
+    let isMakeStylesImported = false;
+    const makeStylesDeclarations = []; // contains constants from makeStyles calls: `const useStyles = makeStyles({})`
+
+    return {
+      ImportDeclaration(node) {
+        if (!isMakeStylesImported) {
+          isMakeStylesImported = isMakeStylesImport(node);
+        }
+      },
+      CallExpression(node) {
+        if (isMakeStylesImported && isMakeStyleCallExpression(node)) {
+          if (!isStylesFile(fileName)) {
+            context.report({
+              messageId: 'foundMakeStylesUsage',
+              node,
+            });
+          }
+        }
+      },
+      VariableDeclaration(node) {
+        if (isStylesFile(fileName)) {
+          node.declarations?.forEach(declaration => {
+            if (
+              isMakeStylesImported &&
+              declaration.init?.type === 'CallExpression' &&
+              isMakeStyleCallExpression(declaration.init)
+            ) {
+              if (declaration.id.type === 'Identifier') {
+                makeStylesDeclarations.push(declaration.id.name);
+              }
+            }
+          });
+        }
+      },
+    };
+  },
+});
diff --git a/yarn.lock b/yarn.lock
index 741894d..8111063 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -10633,10 +10633,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"csstype@npm:^3.0.10, csstype@npm:^3.0.2, csstype@npm:^3.0.5":
-  version: 3.1.1
-  resolution: "csstype@npm:3.1.1"
-  checksum: 1f7b4f5fdd955b7444b18ebdddf3f5c699159f13e9cf8ac9027ae4a60ae226aef9bbb14a6e12ca7dba3358b007cee6354b116e720262867c398de6c955ea451d
+"csstype@npm:^3.0.2, csstype@npm:^3.0.5, csstype@npm:^3.1.2":
+  version: 3.1.2
+  resolution: "csstype@npm:3.1.2"
+  checksum: e1a52e6c25c1314d6beef5168da704ab29c5186b877c07d822bd0806717d9a265e8493a2e35ca7e68d0f5d472d43fac1cdce70fd79fd0853dff81f3028d857b5
   languageName: node
   linkType: hard
 
@@ -14221,7 +14221,7 @@ __metadata:
     browserslist: ^4.19.1
     classnames: 2.3.2
     css-loader: ^6.4.0
-    csstype: ^3.0.10
+    csstype: ^3.1.2
     d3-scale-chromatic: ^3.0.0
     doctoc: 2.2.1
     enhanced-resolve: ^5.8.2


Test patch:
diff --git a/packages/core/src/shorthands/marginBlock.test.ts b/packages/core/src/shorthands/marginBlock.test.ts
new file mode 100644
index 0000000..758c3dc
--- /dev/null
+++ b/packages/core/src/shorthands/marginBlock.test.ts
@@ -0,0 +1,24 @@
+import { marginBlock } from './marginBlock';
+
+describe('marginBlock', () => {
+  it('for a given value', () => {
+    expect(marginBlock('12px')).toEqual({
+      marginBlockStart: '12px',
+      marginBlockEnd: '12px',
+    });
+  });
+
+  it('for given start and end', () => {
+    expect(marginBlock('12px', '24px')).toEqual({
+      marginBlockStart: '12px',
+      marginBlockEnd: '24px',
+    });
+  });
+
+  it('for fallback value arrays', () => {
+    expect(marginBlock(['12px', 0], ['normal', 'var(--customMarginBlock)'])).toEqual({
+      marginBlockStart: ['12px', 0],
+      marginBlockEnd: ['normal', 'var(--customMarginBlock)'],
+    });
+  });
+});
diff --git a/packages/core/src/shorthands/marginInline.test.ts b/packages/core/src/shorthands/marginInline.test.ts
new file mode 100644
index 0000000..2970fbd
--- /dev/null
+++ b/packages/core/src/shorthands/marginInline.test.ts
@@ -0,0 +1,24 @@
+import { marginInline } from './marginInline';
+
+describe('marginInline', () => {
+  it('for a given value', () => {
+    expect(marginInline('12px')).toEqual({
+      marginInlineStart: '12px',
+      marginInlineEnd: '12px',
+    });
+  });
+
+  it('for given start and end', () => {
+    expect(marginInline('12px', '24px')).toEqual({
+      marginInlineStart: '12px',
+      marginInlineEnd: '24px',
+    });
+  });
+
+  it('for fallback value arrays', () => {
+    expect(marginInline(['12px', 0], ['normal', 'var(--customMarginInline)'])).toEqual({
+      marginInlineStart: ['12px', 0],
+      marginInlineEnd: ['normal', 'var(--customMarginInline)'],
+    });
+  });
+});
diff --git a/packages/core/src/shorthands/paddingBlock.test.ts b/packages/core/src/shorthands/paddingBlock.test.ts
new file mode 100644
index 0000000..d6a6748
--- /dev/null
+++ b/packages/core/src/shorthands/paddingBlock.test.ts
@@ -0,0 +1,24 @@
+import { paddingBlock } from './paddingBlock';
+
+describe('paddingBlock', () => {
+  it('for a given value', () => {
+    expect(paddingBlock('12px')).toEqual({
+      paddingBlockStart: '12px',
+      paddingBlockEnd: '12px',
+    });
+  });
+
+  it('for given start and end', () => {
+    expect(paddingBlock('12px', '24px')).toEqual({
+      paddingBlockStart: '12px',
+      paddingBlockEnd: '24px',
+    });
+  });
+
+  it('for fallback value arrays', () => {
+    expect(paddingBlock(['12px', 0], ['normal', 'var(--customPaddingBlock)'])).toEqual({
+      paddingBlockStart: ['12px', 0],
+      paddingBlockEnd: ['normal', 'var(--customPaddingBlock)'],
+    });
+  });
+});
diff --git a/packages/core/src/shorthands/paddingInline.test.ts b/packages/core/src/shorthands/paddingInline.test.ts
new file mode 100644
index 0000000..f5a2f5d
--- /dev/null
+++ b/packages/core/src/shorthands/paddingInline.test.ts
@@ -0,0 +1,24 @@
+import { paddingInline } from './paddingInline';
+
+describe('paddingInline', () => {
+  it('for a given value', () => {
+    expect(paddingInline('12px')).toEqual({
+      paddingInlineStart: '12px',
+      paddingInlineEnd: '12px',
+    });
+  });
+
+  it('for given start and end', () => {
+    expect(paddingInline('12px', '24px')).toEqual({
+      paddingInlineStart: '12px',
+      paddingInlineEnd: '24px',
+    });
+  });
+
+  it('for fallback value arrays', () => {
+    expect(paddingInline(['12px', 0], ['normal', 'var(--customPaddingInline)'])).toEqual({
+      paddingInlineStart: ['12px', 0],
+      paddingInlineEnd: ['normal', 'var(--customPaddingInline)'],
+    });
+  });
+});
diff --git a/packages/eslint-plugin/src/rules/styles-file.test.ts b/packages/eslint-plugin/src/rules/styles-file.test.ts
new file mode 100644
index 0000000..17f9955
--- /dev/null
+++ b/packages/eslint-plugin/src/rules/styles-file.test.ts
@@ -0,0 +1,78 @@
+import { TSESLint } from '@typescript-eslint/utils';
+import { stylesFileRule, RULE_NAME } from './styles-file';
+import * as path from 'path';
+
+const componentFileName = 'packages/components/components-foo/foo.tsx';
+const stylesFileName = 'packages/components/components-foo/foo.styles.ts';
+
+const ruleTester = new TSESLint.RuleTester({
+  parser: path.resolve('./node_modules/@typescript-eslint/parser'),
+  parserOptions: {
+    ecmaVersion: 2018,
+    sourceType: 'module',
+  },
+});
+
+const codeWithFluentNamespaceImport = `
+import * as fluent from '@fluentui/react-components';
+
+export const useStyles = fluent.makeStyles({
+  root: { color: 'blue' },
+});
+`;
+const codeWithMakeStylesImport = `
+import { makeStyles } from '@fluentui/react-components';
+
+export const useStyles = makeStyles({
+  root: { color: 'blue' },
+});
+`;
+
+ruleTester.run(RULE_NAME, stylesFileRule, {
+  valid: [
+    {
+      code: `
+        import { Button } from "@fluentui/react-components";
+        const tag = <Button>test content</Button>;
+      `,
+      filename: componentFileName,
+    },
+    // import and use makeStyles in COMPONENT_NAME.styles.ts is okay
+    {
+      code: codeWithFluentNamespaceImport,
+      filename: stylesFileName,
+    },
+    {
+      code: codeWithMakeStylesImport,
+      filename: stylesFileName,
+    },
+    {
+      code: `
+        import { makeStyles } from '@fluentui/react-components';
+
+        const useStyles = makeStyles({
+          root: { color: 'blue' },
+        });
+        const useStyles1 = makeStyles({
+          root: { color: 'red' },
+        });
+
+        export { useStyles as foo, useStyles1 as bar };
+      `,
+      filename: stylesFileName,
+    },
+  ],
+  invalid: [
+    // Error when import and use makeStyles in component
+    {
+      code: codeWithFluentNamespaceImport,
+      errors: [{ messageId: 'foundMakeStylesUsage' }],
+      filename: componentFileName,
+    },
+    {
+      code: codeWithMakeStylesImport,
+      errors: [{ messageId: 'foundMakeStylesUsage' }],
+      filename: componentFileName,
+    },
+  ],
+});