From 158a40088e68cf7559d50534cd6cb5078410eda8 Mon Sep 17 00:00:00 2001 From: Masaki Morishita Date: Wed, 24 Dec 2025 13:32:07 +0900 Subject: [PATCH 1/2] dx: export type `StyleProps` --- examples/Button.tsx | 12 +++--------- src/index.ts | 4 ++++ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/examples/Button.tsx b/examples/Button.tsx index 363e6ea..aa3c750 100644 --- a/examples/Button.tsx +++ b/examples/Button.tsx @@ -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({ @@ -39,14 +39,8 @@ const button = windctrl({ type ButtonProps = { 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; +} & Omit, keyof StyleProps> & + StyleProps; export function Button({ as, diff --git a/src/index.ts b/src/index.ts index b1caa2f..11b5d79 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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 extends (props?: infer P) => any ? P : never; +export type wcProps = StyleProps; From c880848302ace5b8b1d5780e8936d4c9801f4aa4 Mon Sep 17 00:00:00 2001 From: Masaki Morishita Date: Wed, 24 Dec 2025 14:32:45 +0900 Subject: [PATCH 2/2] docs: type StyleProps --- README.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/README.md b/README.md index 8acceeb..2adbfdd 100644 --- a/README.md +++ b/README.md @@ -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` extracts all variant, trait, and dynamic props from a WindCtrl instance — similar to `VariantProps` in cva. + +```typescript +const button = windctrl({ ... }); + +type ButtonProps = { + as?: T; +} & Omit, keyof StyleProps> + & StyleProps; +``` + +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.