Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,37 @@ const button = windctrl({

The scope classes are automatically prefixed with `group-data-[windctrl-scope=...]/windctrl-scope:` to target the parent's data attribute.

## Type Helpers (`StyleProps`)

When building reusable components, you often want to expose the exact style-related props inferred from a `windctrl()` definition.

WindCtrl exports a small type helper for this purpose:

```typescript
import type { StyleProps } from "windctrl";
```

`StyleProps<typeof styles>` extracts all variant, trait, and dynamic props from a WindCtrl instance — similar to `VariantProps` in cva.

```typescript
const button = windctrl({ ... });

type ButtonProps<T extends ElementType = "button"> = {
as?: T;
} & Omit<ComponentPropsWithoutRef<T>, keyof StyleProps<typeof button>>
& StyleProps<typeof button>;
```

This lets you:

- Avoid manually duplicating variant/trait prop definitions
- Keep component props automatically in sync with styling config
- Refactor styles without touching component typings

> `StyleProps` is optional - you can always define props manually if you prefer.

> `wcProps` is provided as an alias of `StyleProps` for convenience.

## Gotchas

- **Tailwind JIT:** Tailwind only generates CSS for class names it can statically detect. Avoid constructing class strings dynamically unless you safelist them.
Expand Down
12 changes: 3 additions & 9 deletions examples/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";
import { windctrl, dynamic as d, wcn } from "../src/index";
import { windctrl, dynamic as d, wcn, type StyleProps } from "../src/index";
import type { ComponentPropsWithoutRef, ElementType } from "react";

const button = windctrl({
Expand Down Expand Up @@ -39,14 +39,8 @@ const button = windctrl({

type ButtonProps<T extends ElementType = "button"> = {
as?: T;
intent?: "primary" | "secondary" | "destructive" | "outline" | "ghost";
size?: "sm" | "md" | "lg";
traits?:
| Array<"loading" | "glass" | "disabled">
| { loading?: boolean; glass?: boolean; disabled?: boolean };
w?: string | number;
h?: string | number;
} & ComponentPropsWithoutRef<T>;
} & Omit<ComponentPropsWithoutRef<T>, keyof StyleProps<typeof button>> &
StyleProps<typeof button>;

export function Button<T extends ElementType = "button">({
as,
Expand Down
4 changes: 4 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,3 +389,7 @@ export const wc = windctrl;
export function wcn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

// Extract the props type from a windctrl instance
export type StyleProps<T> = T extends (props?: infer P) => any ? P : never;
export type wcProps<T> = StyleProps<T>;