Repository: microsoft/griffel (ts)

Issue:
shorthands: create a function for transition property
transition: ".3s cubic-bezier(.33,0,0,1) all" can't be used with griffel atm

Pull request description:
feat: adds transition shorthand
Fixes #177 

Patch:
diff --git a/apps/website/docs/react/api/shorthands.md b/apps/website/docs/react/api/shorthands.md
index 397f16e..b7f0398 100644
--- a/apps/website/docs/react/api/shorthands.md
+++ b/apps/website/docs/react/api/shorthands.md
@@ -224,3 +224,22 @@ const useClasses = makeStyles({
   },
 });
 ```
+
+### `shorthands.transition`
+
+```js
+import { makeStyles, shorthands } from '@griffel/react';
+
+const useClasses = makeStyles({
+  root: {
+    ...shorthands.transition('inherit'),
+    ...shorthands.transition('margin-right', '2s'),
+    ...shorthands.transition('margin-right', '4s', '1s'),
+    ...shorthands.transition('margin-right', '4s', '1s', 'ease-in'),
+    ...shorthands.transition([
+      ['margin-right', '4s', '1s', 'ease-in'],
+      ['margin-left', '2s', '0s', 'ease-in-out'],
+    ]),
+  },
+});
+```
diff --git a/change/@griffel-core-cca9a4d2-a460-4d77-a750-b6dd381c37f5.json b/change/@griffel-core-cca9a4d2-a460-4d77-a750-b6dd381c37f5.json
new file mode 100644
index 0000000..f5ef2ea
--- /dev/null
+++ b/change/@griffel-core-cca9a4d2-a460-4d77-a750-b6dd381c37f5.json
@@ -0,0 +1,7 @@
+{
+  "type": "minor",
+  "comment": "feat: add transition shorthand",
+  "packageName": "@griffel/core",
+  "email": "bernardo.sunderhus@gmail.com",
+  "dependentChangeType": "patch"
+}
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index de694f7..aa817db 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -18,6 +18,7 @@ import {
   overflow,
   inset,
   outline,
+  transition,
 } from './shorthands/index';
 
 export const shorthands = {
@@ -38,6 +39,7 @@ export const shorthands = {
   overflow,
   inset,
   outline,
+  transition,
 };
 
 export { createDOMRenderer } from './renderer/createDOMRenderer';
diff --git a/packages/core/src/shorthands/index.ts b/packages/core/src/shorthands/index.ts
index aec15e7..1c21249 100644
--- a/packages/core/src/shorthands/index.ts
+++ b/packages/core/src/shorthands/index.ts
@@ -15,3 +15,4 @@ export { padding } from './padding';
 export { overflow } from './overflow';
 export { inset } from './inset';
 export { outline } from './outline';
+export { transition } from './transition';
diff --git a/packages/core/src/shorthands/transition.ts b/packages/core/src/shorthands/transition.ts
new file mode 100644
index 0000000..d4c38be
--- /dev/null
+++ b/packages/core/src/shorthands/transition.ts
@@ -0,0 +1,95 @@
+import type { GriffelStylesStrictCSSObject } from '../types';
+import {
+  TransitionDelayInput,
+  TransitionDurationInput,
+  TransitionPropertyInput,
+  TransitionTimingFunctionInput,
+  TransitionGlobalInput,
+} from './types';
+
+type TransitionInputs = [
+  TransitionPropertyInput,
+  TransitionDurationInput?,
+  TransitionDelayInput?,
+  TransitionTimingFunctionInput?,
+];
+
+type TransitionStyle = Pick<
+  GriffelStylesStrictCSSObject,
+  'transitionProperty' | 'transitionDelay' | 'transitionDuration' | 'transitionTimingFunction'
+>;
+
+export function transition(globalValue: TransitionGlobalInput): TransitionStyle;
+export function transition(property: TransitionPropertyInput, duration: TransitionDurationInput): TransitionStyle;
+export function transition(
+  property: TransitionPropertyInput,
+  duration: TransitionDurationInput,
+  delay: TransitionDelayInput,
+): TransitionStyle;
+export function transition(
+  property: TransitionPropertyInput,
+  duration: TransitionDurationInput,
+  delay: TransitionDelayInput,
+  easingFunction: TransitionTimingFunctionInput,
+): TransitionStyle;
+export function transition(values: TransitionInputs[]): TransitionStyle;
+
+/**
+ * A function that implements expansion for "transition", it's simplified - check usage examples.
+ *
+ * @example
+ *  transition('inherit')
+ *  transition('margin-right', '4s')
+ *  transition('margin-right', '4s', '1s')
+ *  transition('margin-right', '4s', '1s', 'ease-in')
+ *  transition([
+ *    ['margin-right', '4s', '1s', 'ease-in'],
+ *    ['margin-right', '4s', '1s', 'ease-in'],
+ * ])
+ *
+ * See https://developer.mozilla.org/en-US/docs/Web/CSS/transition
+ */
+export function transition(
+  ...values: [TransitionGlobalInput] | TransitionInputs | [TransitionInputs[]]
+): TransitionStyle {
+  if (isTransitionGlobalInputs(values)) {
+    return {
+      transitionDelay: values[0],
+      transitionDuration: values[0],
+      transitionProperty: values[0],
+      transitionTimingFunction: values[0],
+    };
+  }
+  const transitionInputs = normalizeTransitionInputs(values);
+  return transitionInputs.reduce<TransitionStyle>(
+    (acc, [property, duration = '0s', delay = '0s', timingFunction = 'ease'], index) => {
+      if (index === 0) {
+        acc.transitionProperty = property;
+        acc.transitionDuration = duration;
+        acc.transitionDelay = delay;
+        acc.transitionTimingFunction = timingFunction;
+      } else {
+        acc.transitionProperty += `, ${property}`;
+        acc.transitionDuration += `, ${duration}`;
+        acc.transitionDelay += `, ${delay}`;
+        acc.transitionTimingFunction += `, ${timingFunction}`;
+      }
+      return acc;
+    },
+    {},
+  );
+}
+
+const transitionGlobalInputs: TransitionGlobalInput[] = ['-moz-initial', 'inherit', 'initial', 'revert', 'unset'];
+function isTransitionGlobalInputs(
+  values: [TransitionGlobalInput] | TransitionInputs | [TransitionInputs[]],
+): values is [TransitionGlobalInput] {
+  return values.length === 1 && transitionGlobalInputs.includes(values[0] as TransitionGlobalInput);
+}
+
+function normalizeTransitionInputs(transitionInputs: TransitionInputs | [TransitionInputs[]]): TransitionInputs[] {
+  if (transitionInputs.length === 1 && Array.isArray(transitionInputs[0])) {
+    return transitionInputs[0];
+  }
+  return [transitionInputs as TransitionInputs];
+}
diff --git a/packages/core/src/shorthands/types.ts b/packages/core/src/shorthands/types.ts
index 22e37c1..0d6dc05 100644
--- a/packages/core/src/shorthands/types.ts
+++ b/packages/core/src/shorthands/types.ts
@@ -24,3 +24,9 @@ export type PaddingInput = ValueOrArray<CSS.Property.Padding<GriffelStylesCSSVal
 export type InsetInput = ValueOrArray<CSS.Property.Inset<GriffelStylesCSSValue>>;
 
 export type FlexInput = [CSS.Property.Flex, CSS.Property.Flex?, CSS.Property.Flex?];
+
+export type TransitionPropertyInput = CSS.Property.TransitionProperty;
+export type TransitionDurationInput = CSS.Property.TransitionDuration;
+export type TransitionDelayInput = CSS.Property.TransitionDelay;
+export type TransitionTimingFunctionInput = CSS.Property.TransitionTimingFunction;
+export type TransitionGlobalInput = CSS.Globals;
diff --git a/packages/react/README.md b/packages/react/README.md
index 8cdf9aa..098867d 100644
--- a/packages/react/README.md
+++ b/packages/react/README.md
@@ -248,6 +248,25 @@ const useClasses = makeStyles({
 });
 ```
 
+### `shorthands.transition`
+
+```js
+import { makeStyles, shorthands } from '@griffel/react';
+
+const useClasses = makeStyles({
+  root: {
+    ...shorthands.transition('inherit'),
+    ...shorthands.transition('margin-right', '2s'),
+    ...shorthands.transition('margin-right', '4s', '1s'),
+    ...shorthands.transition('margin-right', '4s', '1s', 'ease-in'),
+    ...shorthands.transition([
+      ['margin-right', '4s', '1s', 'ease-in'],
+      ['margin-left', '2s', '0s', 'ease-in-out'],
+    ]),
+  },
+});
+```
+
 ## `makeStaticStyles()`
 
 Creates styles attached to a global selector. Styles can be defined via objects:


Test patch:
diff --git a/packages/core/src/shorthands/transition.test.ts b/packages/core/src/shorthands/transition.test.ts
new file mode 100644
index 0000000..e8ec883
--- /dev/null
+++ b/packages/core/src/shorthands/transition.test.ts
@@ -0,0 +1,52 @@
+import { transition } from './transition';
+
+describe('transition', () => {
+  it('for a global value', () => {
+    expect(transition('initial')).toEqual<ReturnType<typeof transition>>({
+      transitionDelay: 'initial',
+      transitionTimingFunction: 'initial',
+      transitionProperty: 'initial',
+      transitionDuration: 'initial',
+    });
+  });
+
+  it('for a property name and duration', () => {
+    expect(transition('margin', '2s')).toEqual<ReturnType<typeof transition>>({
+      transitionProperty: 'margin',
+      transitionDuration: '2s',
+      transitionDelay: '0s',
+      transitionTimingFunction: 'ease',
+    });
+  });
+
+  it('for a given name, duration and delay', () => {
+    expect(transition('margin', '2s', '1s')).toEqual<ReturnType<typeof transition>>({
+      transitionProperty: 'margin',
+      transitionDuration: '2s',
+      transitionDelay: '1s',
+      transitionTimingFunction: 'ease',
+    });
+  });
+
+  it('for a given name, duration, delay and easing-function', () => {
+    expect(transition('margin', '2s', '1s', 'ease-in')).toEqual<ReturnType<typeof transition>>({
+      transitionProperty: 'margin',
+      transitionDuration: '2s',
+      transitionDelay: '1s',
+      transitionTimingFunction: 'ease-in',
+    });
+  });
+  it('for multiple properties', () => {
+    expect(
+      transition([
+        ['margin', '2s', '1s', 'ease-in'],
+        ['padding', '3s', '2s', 'ease-out'],
+      ]),
+    ).toEqual<ReturnType<typeof transition>>({
+      transitionProperty: 'margin, padding',
+      transitionDuration: '2s, 3s',
+      transitionDelay: '1s, 2s',
+      transitionTimingFunction: 'ease-in, ease-out',
+    });
+  });
+});