Skip to content

Commit 145c621

Browse files
authored
Unify type of static css prop and dynamic css mixin (#257)
1 parent 171898f commit 145c621

File tree

8 files changed

+61
-24
lines changed

8 files changed

+61
-24
lines changed

.changeset/smart-cars-eat.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"next-yak": patch
3+
---
4+
5+
Refactor CSS prop types to improve type inference and consistency
6+
7+
- Added new type `CSSProp` to be used by custom components to receive the `css` prop in a somewhat typesafe way
8+
- Updated `css` function to return `ComponentStyle` only

packages/next-yak/runtime/__tests__/cssPropTest.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/** @jsxImportSource next-yak */
22
// this is only a type check file and should not be executed
33

4-
import { css, styled } from "next-yak";
4+
import { css, CSSProp, styled } from "next-yak";
55
import { CSSProperties } from "react";
66

77
declare module "next-yak" {
@@ -28,9 +28,7 @@ const NestedComponentWithCssProp = () => (
2828
</div>
2929
);
3030

31-
const ComponentThatTakesCssProp = (p: {
32-
css: { className: string; style?: CSSProperties };
33-
}) => <div {...p}>anything</div>;
31+
const ComponentThatTakesCssProp = (p: CSSProp) => <div {...p}>anything</div>;
3432

3533
const ComponentWithCssPropAsProp = () => {
3634
return <ComponentThatTakesCssProp css={css``} />;

packages/next-yak/runtime/__tests__/typeTest.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,3 +297,19 @@ const SelectorMixinsShouldNotAlterType = () => {
297297

298298
<X />;
299299
};
300+
301+
const InferenceShouldWorkWithComplexTypes = () => {
302+
const Title = styled<React.ComponentPropsWithRef<"strong" | "h1">>(
303+
{} as any,
304+
)<{
305+
$primary: boolean;
306+
}>`
307+
${({ $primary }) => {
308+
if ($primary) {
309+
return css`
310+
color: red;
311+
`;
312+
}
313+
}}
314+
`;
315+
};

packages/next-yak/runtime/cssLiteral.tsx

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,32 @@ import type { YakTheme } from "./index.d.ts";
33

44
export const yakComponentSymbol = Symbol("yak");
55

6-
type ComponentStyles<TProps> = (props: TProps) => {
6+
export type ComponentStyles<TProps> = (props: TProps) => {
77
className: string;
88
style?: {
99
[key: string]: string;
1010
};
1111
};
1212

13-
export type StaticCSSProp = {
14-
className: string;
15-
style?: CSSProperties;
13+
/**
14+
* Convenience type to specify the CSS prop type.
15+
* This type is used to allow the css prop on components.
16+
*
17+
* @example
18+
* ```tsx
19+
* const ComponentThatTakesCssProp = (p: CSSProp) =>
20+
* <div {...p}>anything</div>;
21+
* ```
22+
*/
23+
export type CSSProp = {
24+
/** This prop is transformed during compilation and doesn't exist at runtime. */
25+
css?: ComponentStyles<Record<keyof any, never>>;
26+
/** The css prop will return the name of the class and automatically merged with an existing class */
27+
className?: string;
28+
/** The css prop will return the style object and automatically merged with an existing style */
29+
style?: {
30+
[key: string]: string;
31+
};
1632
};
1733

1834
export type CSSInterpolation<TProps> =
@@ -22,7 +38,6 @@ export type CSSInterpolation<TProps> =
2238
| null
2339
| false
2440
| ComponentStyles<TProps>
25-
| StaticCSSProp
2641
| {
2742
// type only identifier to allow targeting components
2843
// e.g. styled.svg`${Button}:hover & { fill: red; }`
@@ -61,10 +76,8 @@ export type PropsToClassNameFn = (props: unknown) =>
6176
export function css<TProps>(
6277
styles: TemplateStringsArray,
6378
...values: CSSInterpolation<NoInfer<TProps> & { theme: YakTheme }>[]
64-
): TProps extends object ? ComponentStyles<TProps> : StaticCSSProp;
65-
export function css<TProps>(
66-
...args: Array<any>
67-
): TProps extends object ? ComponentStyles<TProps> : StaticCSSProp {
79+
): ComponentStyles<TProps>;
80+
export function css<TProps>(...args: Array<any>): ComponentStyles<TProps> {
6881
const classNames: string[] = [];
6982
const dynamicCssFunctions: PropsToClassNameFn[] = [];
7083
const style: Record<string, string> = {};
@@ -109,11 +122,9 @@ export function css<TProps>(
109122
// Non Dynamic CSS
110123
if (dynamicCssFunctions.length === 0) {
111124
const className = classNames.join(" ");
112-
// @ts-expect-error - Conditional return types are tricky in the implementation and generate false positives
113125
return () => ({ className, style });
114126
}
115127

116-
// @ts-expect-error - Conditional return types are tricky in the implementation and generate false positives
117128
return (props: unknown) => {
118129
const allClassNames: string[] = [...classNames];
119130
const allStyles: Record<string, string> = { ...style };

packages/next-yak/runtime/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
export { useTheme, YakThemeProvider } from "next-yak/context";
2828
export type { YakTheme } from "./context/index.d.ts";
2929

30-
export { css } from "./mocks/cssLiteral.js";
31-
export { styled } from "./mocks/styled.js";
32-
export { keyframes } from "./mocks/keyframes.js";
3330
export { atoms } from "./atoms.js";
31+
export { css, type CSSProp } from "./mocks/cssLiteral.js";
32+
export { keyframes } from "./mocks/keyframes.js";
33+
export { styled } from "./mocks/styled.js";

packages/next-yak/runtime/jsx-runtime.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import ReactJSXRuntime from "react/jsx-runtime";
2-
import type { StaticCSSProp } from "./mocks/cssLiteral.js";
2+
import type { ComponentStyles } from "./mocks/cssLiteral.js";
33

44
const Fragment = ReactJSXRuntime.Fragment;
55
const jsx = ReactJSXRuntime.jsx;
@@ -18,9 +18,9 @@ export declare namespace YakJSX {
1818
React.JSX.IntrinsicClassAttributes<T>;
1919
export type IntrinsicElements = {
2020
[K in keyof React.JSX.IntrinsicElements]: React.JSX.IntrinsicElements[K] & {
21-
css?: StaticCSSProp;
21+
css?: ComponentStyles<Record<keyof any, never>>;
2222
};
2323
};
2424
}
2525

26-
export { type YakJSX as JSX, Fragment, jsx, jsxs };
26+
export { Fragment, jsx, jsxs, type YakJSX as JSX };

packages/next-yak/runtime/mocks/cssLiteral.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import type { css as cssInternal, PropsToClassNameFn } from "../cssLiteral.js";
22

3-
export type { StaticCSSProp, CSSInterpolation } from "../cssLiteral.js";
3+
export type {
4+
ComponentStyles,
5+
CSSInterpolation,
6+
CSSProp,
7+
} from "../cssLiteral.js";
48

59
/**
610
* Allows to use CSS styles in a styled or css block

packages/next-yak/runtime/styled.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {
22
CSSInterpolation,
3-
StaticCSSProp,
3+
ComponentStyles,
44
css,
55
yakComponentSymbol,
66
} from "./cssLiteral.js";
@@ -83,7 +83,7 @@ type YakComponent<
8383
TAttrsOut extends AttrsMerged<T, TAttrsIn> = AttrsMerged<T, TAttrsIn>,
8484
> = React.FunctionComponent<
8585
T & {
86-
css?: StaticCSSProp;
86+
css?: ComponentStyles<Record<keyof any, never>>;
8787
}
8888
> & {
8989
[yakComponentSymbol]: [

0 commit comments

Comments
 (0)