diff --git a/.cursor/rules/form-component-patterns.mdc b/.cursor/rules/form-component-patterns.mdc new file mode 100644 index 00000000..75c41cb5 --- /dev/null +++ b/.cursor/rules/form-component-patterns.mdc @@ -0,0 +1,218 @@ +--- +type: Always +description: Rules for form component integration patterns in the lambda-curry/forms repository +--- + +You are an expert in React Hook Form, Remix Hook Form, Zod validation, and form component architecture for the lambda-curry/forms monorepo. + +# Form Component Integration Patterns + +## Core Principles +- All form components must integrate seamlessly with Remix Hook Form +- Use Zod schemas for validation with proper TypeScript inference +- Follow the wrapper pattern for consistent component composition +- Maintain separation between UI components and form-aware components +- Ensure proper error handling and validation feedback + +## Required Imports for Form Components +```typescript +// Remix Hook Form integration +import { useRemixFormContext } from 'remix-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; + +// Form components +import { FormControl, FormDescription, FormLabel, FormMessage } from './form'; + +// Base UI components +import { ComponentName as BaseComponentName } from '../ui/component-name'; +``` + +## Form Schema Pattern +Always define Zod schemas with proper error messages: +```typescript +const formSchema = z.object({ + fieldName: z.string().min(1, 'Field is required'), + email: z.string().email('Invalid email address'), + price: z.string().min(1, 'Price is required'), +}); + +type FormData = z.infer; +``` + +## Wrapper Component Pattern +Follow this pattern for all form-aware components: +```typescript +export type ComponentNameProps = Omit; + +export function ComponentName(props: ComponentNameProps) { + const { control } = useRemixFormContext(); + + // Merge provided components with default form components + const defaultComponents = { + FormControl, + FormLabel, + FormDescription, + FormMessage, + }; + + const components = { + ...defaultComponents, + ...props.components, + }; + + return ; +} +``` + +## Component Composition Pattern +For UI components that accept form integration: +```typescript +export interface ComponentNameProps extends Omit { + control?: Control; + name: FieldPath; + label?: string; + description?: string; + components?: Partial & { + Input?: React.ComponentType; + }; + className?: string; +} + +export const ComponentName = ({ + control, + name, + label, + description, + className, + components, + ...props +}: ComponentNameProps) => { + const InputComponent = components?.Input || DefaultInput; + + return ( + ( + + {label && {label}} + + + + {description && {description}} + {fieldState.error && ( + {fieldState.error.message} + )} + + )} + /> + ); +}; +``` + +## Form Setup Pattern +Use this pattern for form initialization: +```typescript +const ControlledComponentExample = () => { + const fetcher = useFetcher<{ message: string }>(); + const methods = useRemixForm({ + resolver: zodResolver(formSchema), + defaultValues: { + // Provide sensible defaults + }, + fetcher, + submitConfig: { action: '/', method: 'post' }, + }); + + return ( + + + {/* Form components */} + + + ); +}; +``` + +## Validation Patterns + +### Client-Side Validation +- Use Zod schemas for all form validation +- Provide clear, user-friendly error messages +- Validate on blur and submit, not on every keystroke + +### Server-Side Validation +```typescript +export const action = async ({ request }: ActionFunctionArgs) => { + const { data, errors } = await getValidatedFormData( + request, + zodResolver(formSchema) + ); + + if (errors) return { errors }; + + // Additional server-side validation + if (data.username === 'taken') { + return { + errors: { + username: { message: 'Username is already taken' } + } + }; + } + + return { message: 'Success!' }; +}; +``` + +## Error Handling Best Practices +- Always display field-level errors using FormMessage +- Handle both client-side and server-side validation errors +- Provide loading states during form submission +- Clear errors appropriately when fields are corrected + +## Component Naming Conventions +- Form-aware components: `ComponentName` (e.g., `TextField`, `Checkbox`) +- Base UI components: `ComponentName` in ui/ directory +- Props interfaces: `ComponentNameProps` +- Form schemas: `formSchema` or `componentNameSchema` + +## File Organization +``` +packages/components/src/ +├── remix-hook-form/ # Form-aware wrapper components +│ ├── text-field.tsx +│ ├── checkbox.tsx +│ └── index.ts +└── ui/ # Base UI components + ├── text-field.tsx + ├── checkbox.tsx + └── index.ts +``` + +## Required Exports +Always export both the component and its props type: +```typescript +export { ComponentName }; +export type { ComponentNameProps }; +``` + +## Performance Considerations +- Use React.memo for expensive form components +- Avoid unnecessary re-renders by properly structuring form state +- Consider field-level subscriptions for large forms + +## Accessibility Requirements +- All form fields must have proper labels +- Use ARIA attributes for complex form interactions +- Ensure keyboard navigation works correctly +- Provide clear error announcements for screen readers + +## Testing Integration +- Form components should work with the existing Storybook testing patterns +- Test both valid and invalid form states +- Verify server-side validation integration +- Test component composition and customization + +Remember: Form components are the core of this library. Every form component should be intuitive, accessible, and integrate seamlessly with the Remix Hook Form + Zod validation pattern. + diff --git a/.cursor/rules/monorepo-organization.mdc b/.cursor/rules/monorepo-organization.mdc new file mode 100644 index 00000000..84ec5a7b --- /dev/null +++ b/.cursor/rules/monorepo-organization.mdc @@ -0,0 +1,363 @@ +--- +type: Always +description: Monorepo structure and import conventions for the lambda-curry/forms repository +--- + +You are an expert in monorepo architecture, package management, and TypeScript module organization. + +# Monorepo Organization & Import Conventions + +## Core Principles +- Maintain clear separation between packages and apps +- Use consistent import patterns across the monorepo +- Follow package naming conventions +- Organize exports for optimal tree-shaking +- Ensure proper dependency management + +## Project Structure +``` +lambda-curry/forms/ +├── apps/ +│ └── docs/ # Storybook documentation app +│ ├── src/ +│ │ ├── remix-hook-form/ # Story files +│ │ └── lib/ # Storybook utilities +│ └── package.json +├── packages/ +│ └── components/ # Main component library +│ ├── src/ +│ │ ├── remix-hook-form/ # Form-aware components +│ │ ├── ui/ # Base UI components +│ │ └── index.ts # Main export file +│ └── package.json +├── package.json # Root package.json +└── turbo.json # Turbo configuration +``` + +## Package Import Patterns + +### External Package Imports +```typescript +// ✅ Use package name imports for published packages +import { TextField } from '@lambdacurry/forms/remix-hook-form'; +import { Button } from '@lambdacurry/forms/ui'; + +// ✅ Import from specific entry points +import { TextField } from '@lambdacurry/forms/remix-hook-form/text-field'; +import { Button } from '@lambdacurry/forms/ui/button'; +``` + +### Internal Package Imports +```typescript +// ✅ Use relative imports within the same package +import { FormControl } from './form'; +import { cn } from './utils'; + +// ✅ Use relative imports for sibling directories +import { Button } from '../ui/button'; +import { TextField as BaseTextField } from '../ui/text-field'; +``` + +### Cross-Package Dependencies +```typescript +// ✅ In apps/docs - import from package name +import { TextField } from '@lambdacurry/forms/remix-hook-form'; +import { Button } from '@lambdacurry/forms/ui'; + +// ❌ Don't use relative imports across packages +import { TextField } from '../../packages/components/src/remix-hook-form/text-field'; +``` + +## Export Conventions + +### Package Entry Points +```typescript +// packages/components/src/index.ts +export * from './ui'; +export * from './remix-hook-form'; + +// packages/components/src/ui/index.ts +export { Button } from './button'; +export type { ButtonProps } from './button'; +export { Input } from './input'; +export type { InputProps } from './input'; + +// packages/components/src/remix-hook-form/index.ts +export { TextField } from './text-field'; +export type { TextFieldProps } from './text-field'; +export { Checkbox } from './checkbox'; +export type { CheckboxProps } from './checkbox'; +``` + +### Specific Entry Points (package.json) +```json +{ + "name": "@lambdacurry/forms", + "exports": { + ".": { + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "./remix-hook-form": { + "import": { + "types": "./dist/remix-hook-form/index.d.ts", + "default": "./dist/remix-hook-form/index.js" + } + }, + "./ui": { + "import": { + "types": "./dist/ui/index.d.ts", + "default": "./dist/ui/index.js" + } + } + } +} +``` + +### Component Exports +```typescript +// ✅ Always export both component and props type +export { ComponentName }; +export type { ComponentNameProps }; + +// ✅ Use named exports for better tree-shaking +export const ComponentName = () => { /* ... */ }; + +// ❌ Avoid default exports for components +export default ComponentName; +``` + +## File Naming Conventions + +### Component Files +``` +// ✅ Use kebab-case for file names +text-field.tsx +data-table.tsx +dropdown-menu.tsx + +// ✅ Match component name in PascalCase +// text-field.tsx exports TextField +// data-table.tsx exports DataTable +``` + +### Directory Structure +``` +src/ +├── remix-hook-form/ # Form-aware wrapper components +│ ├── text-field.tsx # TextField component +│ ├── checkbox.tsx # Checkbox component +│ ├── form.tsx # Form utilities +│ └── index.ts # Package exports +├── ui/ # Base UI components +│ ├── button.tsx # Button component +│ ├── input.tsx # Input component +│ ├── utils/ # Utility functions +│ │ ├── cn.ts # Class name utility +│ │ └── index.ts # Utility exports +│ └── index.ts # Package exports +└── index.ts # Main package export +``` + +## Dependency Management + +### Package Dependencies +```json +// packages/components/package.json +{ + "peerDependencies": { + "react": "^19.0.0", + "remix-hook-form": "7.0.0" + }, + "dependencies": { + "@radix-ui/react-slot": "^1.1.2", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1" + }, + "devDependencies": { + "@types/react": "^19.0.0", + "typescript": "^5.7.2" + } +} +``` + +### Workspace Dependencies +```json +// apps/docs/package.json +{ + "dependencies": { + "@lambdacurry/forms": "*" // Use workspace version + } +} +``` + +## TypeScript Configuration + +### Path Mapping +```json +// tsconfig.json +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@lambdacurry/forms": ["./packages/components/src"], + "@lambdacurry/forms/*": ["./packages/components/src/*"] + } + } +} +``` + +### Package TypeScript Config +```json +// packages/components/tsconfig.json +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src/**/*"], + "exclude": ["dist", "node_modules"] +} +``` + +## Build Configuration + +### Turbo Configuration +```json +// turbo.json +{ + "pipeline": { + "build": { + "dependsOn": ["^build"], + "outputs": ["dist/**"] + }, + "dev": { + "cache": false, + "persistent": true + }, + "test": { + "dependsOn": ["build"] + } + } +} +``` + +### Vite Build Config +```typescript +// packages/components/vite.config.ts +import { defineConfig } from 'vite'; +import { resolve } from 'path'; +import dts from 'vite-plugin-dts'; + +export default defineConfig({ + plugins: [dts()], + build: { + lib: { + entry: { + index: resolve(__dirname, 'src/index.ts'), + 'remix-hook-form/index': resolve(__dirname, 'src/remix-hook-form/index.ts'), + 'ui/index': resolve(__dirname, 'src/ui/index.ts'), + }, + formats: ['es'], + }, + rollupOptions: { + external: ['react', 'react-dom', 'remix-hook-form'], + }, + }, +}); +``` + +## Import Organization + +### Import Order +```typescript +// 1. External library imports +import * as React from 'react'; +import { type VariantProps, cva } from 'class-variance-authority'; +import { Slot } from '@radix-ui/react-slot'; + +// 2. Internal package imports (same package) +import { cn } from './utils'; +import { FormControl } from './form'; + +// 3. Cross-package imports +import { Button } from '@lambdacurry/forms/ui'; + +// 4. Type-only imports (grouped separately) +import type { ComponentProps } from 'react'; +import type { FormFieldProps } from './types'; +``` + +### Re-export Patterns +```typescript +// ✅ Re-export with explicit names +export { Button } from './button'; +export { Input } from './input'; + +// ✅ Re-export types explicitly +export type { ButtonProps } from './button'; +export type { InputProps } from './input'; + +// ✅ Barrel exports for convenience +export * from './components'; + +// ❌ Avoid mixing default and named exports +export { default as Button } from './button'; +``` + +## Versioning Strategy + +### Package Versioning +```json +// Use consistent versioning across packages +{ + "name": "@lambdacurry/forms", + "version": "0.15.1" +} +``` + +### Changesets Integration +```markdown +--- +"@lambdacurry/forms": minor +--- + +Add new TextField component with validation support +``` + +## Documentation Structure +``` +docs/ +├── components/ # Component documentation +│ ├── text-field.mdx +│ └── button.mdx +├── guides/ # Usage guides +│ ├── getting-started.mdx +│ └── form-validation.mdx +└── examples/ # Code examples + ├── basic-form.tsx + └── advanced-form.tsx +``` + +## Best Practices + +### Package Boundaries +- Keep form-aware components separate from base UI components +- Don't import from `remix-hook-form` package in `ui` components +- Use composition over inheritance for cross-package functionality + +### Performance +- Use tree-shaking friendly exports +- Avoid circular dependencies +- Keep bundle sizes minimal with proper externals + +### Maintenance +- Use consistent naming across packages +- Document breaking changes in changelogs +- Maintain backward compatibility when possible + +Remember: A well-organized monorepo makes development faster and more predictable. Every import should be intentional and follow the established patterns to maintain consistency across the codebase. + diff --git a/.cursor/rules/react-typescript-patterns.mdc b/.cursor/rules/react-typescript-patterns.mdc new file mode 100644 index 00000000..bf89a4d4 --- /dev/null +++ b/.cursor/rules/react-typescript-patterns.mdc @@ -0,0 +1,395 @@ +--- +type: Always +description: React and TypeScript component architecture patterns for the lambda-curry/forms repository +--- + +You are an expert in React 19, TypeScript, modern React patterns, and component library architecture. + +# React + TypeScript Component Architecture + +## Core Principles +- Use functional components with TypeScript interfaces +- Prefer composition over inheritance +- Write self-documenting code with descriptive names +- Follow React 19 best practices and modern patterns +- Maintain consistent component structure and naming + +## Component Structure Pattern +Organize components with this consistent structure: +```typescript +// 1. Imports (external libraries first, then internal) +import * as React from 'react'; +import { type VariantProps, cva } from 'class-variance-authority'; +import { Slot } from '@radix-ui/react-slot'; + +// 2. Internal imports +import { cn } from './utils'; + +// 3. Type definitions and interfaces +export interface ComponentProps extends React.HTMLAttributes { + variant?: 'default' | 'secondary'; + size?: 'sm' | 'md' | 'lg'; + asChild?: boolean; +} + +// 4. Component variants (if using CVA) +const componentVariants = cva( + 'base-classes', + { + variants: { + variant: { + default: 'default-styles', + secondary: 'secondary-styles', + }, + size: { + sm: 'small-styles', + md: 'medium-styles', + lg: 'large-styles', + }, + }, + defaultVariants: { + variant: 'default', + size: 'md', + }, + } +); + +// 5. Component implementation (React 19 - no forwardRef needed!) +export const Component = ({ className, variant, size, asChild = false, ref, ...props }: ComponentProps) => { + const Comp = asChild ? Slot : 'div'; + + return ( + + ); +}; + +// 6. Display name +Component.displayName = 'Component'; +``` + +## React 19 Ref Patterns + +### Direct Ref Props (No More forwardRef!) +```typescript +// ✅ React 19 - ref as a direct prop +export interface ButtonProps extends React.ButtonHTMLAttributes { + variant?: 'default' | 'destructive' | 'outline'; + size?: 'default' | 'sm' | 'lg'; + asChild?: boolean; +} + +export const Button = ({ className, variant, size, asChild = false, ref, ...props }: ButtonProps) => { + const Comp = asChild ? Slot : 'button'; + + return ( + + ); +}; + +// Usage - ref works directly! +const MyComponent = () => { + const buttonRef = React.useRef(null); + + return ; +}; +``` + +### Ref Cleanup Functions (React 19 Feature) +```typescript +// ✅ React 19 - ref cleanup functions +export const AutoFocusInput = ({ ref, ...props }: InputProps) => { + return ( + { + // Set the ref + if (typeof ref === 'function') { + ref(node); + } else if (ref) { + ref.current = node; + } + + // Auto-focus when mounted + if (node) { + node.focus(); + } + + // Return cleanup function (React 19 feature) + return () => { + if (node) { + node.blur(); + } + }; + }} + {...props} + /> + ); +}; +``` + +### useRef Patterns for React 19 +```typescript +// ✅ Modern useRef patterns +export function useImperativeActions() { + const inputRef = React.useRef(null); + + const focus = React.useCallback(() => { + inputRef.current?.focus(); + }, []); + + const clear = React.useCallback(() => { + if (inputRef.current) { + inputRef.current.value = ''; + inputRef.current.focus(); + } + }, []); + + const selectAll = React.useCallback(() => { + inputRef.current?.select(); + }, []); + + return { inputRef, focus, clear, selectAll }; +} + +// Usage +const SearchInput = () => { + const { inputRef, focus, clear } = useImperativeActions(); + + return ( +
+ + + +
+ ); +}; +``` + +## TypeScript Interface Conventions + +### Props Interfaces +```typescript +// Extend HTML attributes when appropriate +export interface ButtonProps extends React.ButtonHTMLAttributes { + variant?: 'default' | 'destructive' | 'outline'; + size?: 'default' | 'sm' | 'lg'; + asChild?: boolean; +} + +// Use descriptive names with auxiliary verbs +export interface FormFieldProps { + isLoading?: boolean; + hasError?: boolean; + isRequired?: boolean; + isDisabled?: boolean; +} + +// Prefer interfaces over types for component props +export interface ComponentProps { + // Use specific types instead of any + onValueChange?: (value: string) => void; + // Use union types for controlled options + variant: 'primary' | 'secondary' | 'ghost'; +} +``` + +### Generic Type Patterns +```typescript +// For form components that work with any field type +export interface FieldProps { + value?: T; + onChange?: (value: T) => void; + defaultValue?: T; +} + +// For components that accept custom renderers +export interface ListProps { + items: T[]; + renderItem: (item: T, index: number) => React.ReactNode; + keyExtractor: (item: T) => string; +} +``` + +## Component Composition Patterns + +### Compound Components +```typescript +// Main component +export const Card = ({ className, ref, ...props }: CardProps) => ( +
+); + +// Sub-components +export const CardHeader = ({ className, ref, ...props }: CardHeaderProps) => ( +
+); + +export const CardContent = ({ className, ref, ...props }: CardContentProps) => ( +
+); + +// Attach sub-components +Card.Header = CardHeader; +Card.Content = CardContent; +``` + +### Polymorphic Components (React 19 Style) +```typescript +type AsChildProps = { + asChild?: boolean; + as?: T; +} & React.ComponentPropsWithoutRef; + +export const Polymorphic = ({ + asChild, + as, + ref, + ...props +}: AsChildProps) => { + const Component = asChild ? Slot : (as || 'div'); + return ; +}; +``` + +## Hook Patterns + +### React 19 use() Hook for Async Operations +```typescript +// ✅ React 19 - use() hook for promises +import { use } from 'react'; + +interface User { + id: string; + name: string; + email: string; +} + +async function fetchUser(id: string): Promise { + const response = await fetch(`/api/users/${id}`); + return response.json(); +} + +// Component using the new use() hook +export const UserProfile = ({ userId }: { userId: string }) => { + // React 19 - use() hook handles promises directly + const user = use(fetchUser(userId)); + + return ( +
+

{user.name}

+

{user.email}

+
+ ); +}; + +// Wrap with Suspense for loading states +export const UserProfileWithSuspense = ({ userId }: { userId: string }) => ( + Loading user...
}> + + +); +``` + +### Context Patterns +```typescript +// Create strongly typed context +interface FormContextValue { + isSubmitting: boolean; + errors: Record; + setValue: (name: string, value: unknown) => void; +} + +const FormContext = React.createContext(null); + +// Custom hook for context consumption +export function useFormContext() { + const context = React.useContext(FormContext); + if (!context) { + throw new Error('useFormContext must be used within a FormProvider'); + } + return context; +} +``` + +## Event Handler Patterns +```typescript +// Use descriptive event handler names +interface ComponentProps { + onValueChange?: (value: string) => void; + onSelectionChange?: (selectedIds: string[]) => void; + onSubmit?: (data: FormData) => void | Promise; + onError?: (error: Error) => void; +} + +## Naming Conventions +- **Components**: PascalCase (`TextField`, `DataTable`) +- **Props interfaces**: ComponentName + Props (`TextFieldProps`) +- **Hooks**: camelCase starting with 'use' (`useFormValidation`) +- **Event handlers**: camelCase starting with 'handle' (`handleSubmit`) +- **Boolean props**: Use auxiliary verbs (`isLoading`, `hasError`, `canEdit`) +- **Files**: kebab-case (`text-field.tsx`, `data-table.tsx`) + +## File Organization +``` +src/ +├── components/ +│ ├── ui/ # Base UI components +│ │ ├── button.tsx +│ │ └── index.ts +│ └── remix-hook-form/ # Form-aware components +│ ├── text-field.tsx +│ └── index.ts +├── hooks/ # Custom hooks +│ ├── use-form-validation.ts +│ └── index.ts +└── utils/ # Utility functions + ├── cn.ts + └── index.ts +``` + +## Export Patterns +```typescript +// Named exports for components and types +export { Button } from './button'; +export type { ButtonProps } from './button'; + +// Re-export from index files +export * from './button'; +export * from './input'; + +// Default exports only for single-purpose modules +export default function utilityFunction() { + // ... +} +``` + +## Documentation Requirements +```typescript +/** + * A flexible button component that supports multiple variants and sizes. + * + * @example + * ```tsx + * + * ``` + */ +export interface ButtonProps { + /** The visual style variant */ + variant?: 'primary' | 'secondary' | 'ghost'; + /** The size of the button */ + size?: 'sm' | 'md' | 'lg'; + /** Click event handler */ + onClick?: (event: React.MouseEvent) => void; +} +``` + +Remember: Write components that are predictable, composable, and maintainable. Every component should have a clear purpose and follow consistent patterns that make the codebase easy to understand and extend. React 19 simplifies ref handling significantly - embrace the new patterns! diff --git a/.cursor/rules/ui-component-patterns.mdc b/.cursor/rules/ui-component-patterns.mdc new file mode 100644 index 00000000..06fb49e2 --- /dev/null +++ b/.cursor/rules/ui-component-patterns.mdc @@ -0,0 +1,287 @@ +--- +type: Always +description: UI component patterns for Radix UI, Tailwind CSS 4, and component architecture specific to lambda-curry/forms +--- + +You are an expert in Radix UI, Tailwind CSS 4, class-variance-authority (CVA), and accessible component design for the lambda-curry/forms component library. + +# UI Component Patterns + +## Core Principles +- Build accessible-first components using Radix UI primitives +- Use Tailwind CSS 4 with modern CSS features and performance optimizations +- Follow consistent component architecture patterns +- Implement variant patterns with class-variance-authority +- Follow the Slot pattern for polymorphic components +- Focus only on components that exist in the lambda-curry/forms library + +## Project-Specific Component Stack +Based on your actual dependencies and components: + +### Radix UI Components Used +- `@radix-ui/react-alert-dialog` +- `@radix-ui/react-avatar` +- `@radix-ui/react-checkbox` +- `@radix-ui/react-dialog` +- `@radix-ui/react-dropdown-menu` +- `@radix-ui/react-label` +- `@radix-ui/react-popover` +- `@radix-ui/react-radio-group` +- `@radix-ui/react-scroll-area` +- `@radix-ui/react-separator` +- `@radix-ui/react-slot` +- `@radix-ui/react-switch` +- `@radix-ui/react-tooltip` + +### Existing UI Components +- Badge, Button, Checkbox Field +- Command, Data Table +- Date Picker, Dropdown Menu +- Form components, Label +- OTP Input, Popover +- Radio Group, Select +- Separator, Switch +- Table, Text Field, Textarea + +## Tailwind CSS 4 Features & Patterns + +### Modern CSS Custom Properties +```typescript +// ✅ Tailwind 4 - CSS custom properties for theming +const themeClasses = { + // Use CSS custom properties directly + colors: { + primary: 'bg-primary text-primary-foreground', + secondary: 'bg-secondary text-secondary-foreground', + destructive: 'bg-destructive text-destructive-foreground', + muted: 'bg-muted text-muted-foreground', + }, + // Modern container queries + container: 'container @container', + // CSS Grid improvements + grid: 'grid grid-cols-[repeat(auto-fit,minmax(250px,1fr))]', +}; +``` + +### Tailwind 4 Performance Optimizations +```typescript +// ✅ Use Tailwind 4's improved arbitrary value syntax +const modernClasses = cn( + // Improved arbitrary values + 'w-[clamp(200px,50vw,400px)]', + // Container queries + '@md:grid-cols-2 @lg:grid-cols-3', + // Modern CSS functions + 'h-[max(200px,20vh)]', + 'gap-[max(1rem,2vw)]' +); +``` + +## Component Architecture Pattern + +### Standard Component Structure +```typescript +// ✅ Consistent pattern used in lambda-curry/forms +import * as React from 'react'; +import { Slot } from '@radix-ui/react-slot'; +import { type VariantProps, cva } from 'class-variance-authority'; +import { cn } from '@/lib/utils'; + +// Variant pattern following your existing components +const buttonVariants = cva( + // Base classes optimized for Tailwind 4 + 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', + { + variants: { + variant: { + default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90', + destructive: 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90', + outline: 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground', + secondary: 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80', + ghost: 'hover:bg-accent hover:text-accent-foreground', + link: 'text-primary underline-offset-4 hover:underline', + }, + size: { + default: 'h-9 px-4 py-2', + sm: 'h-8 rounded-md px-3 text-xs', + lg: 'h-10 rounded-md px-8', + icon: 'h-9 w-9', + }, + }, + defaultVariants: { + variant: 'default', + size: 'default', + }, + } +); + +// React 19 pattern (no forwardRef needed) +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; +} + +export const Button = ({ className, variant, size, asChild = false, ...props }: ButtonProps) => { + const Comp = asChild ? Slot : 'button'; + return ( + + ); +}; +Button.displayName = 'Button'; +``` + +## Radix UI Integration Patterns + +### Dropdown Menu Pattern (Based on Your Components) +```typescript +import * as React from 'react'; +import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'; +import { Check, ChevronRight, Circle } from 'lucide-react'; +import { cn } from '@/lib/utils'; + +// Following your existing dropdown-menu.tsx pattern +const DropdownMenu = DropdownMenuPrimitive.Root; +const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger; +const DropdownMenuGroup = DropdownMenuPrimitive.Group; +const DropdownMenuPortal = DropdownMenuPrimitive.Portal; +const DropdownMenuSub = DropdownMenuPrimitive.Sub; +const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; + +// Content with Tailwind 4 optimizations +const DropdownMenuContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => ( + + + +)); +DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName; + +export { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuGroup, + // ... other exports +}; +``` + +## Responsive Design Patterns + +### Container Queries with Tailwind 4 +```typescript +// ✅ Modern responsive design for your components +const responsiveClasses = cn( + // Traditional breakpoints + 'text-sm md:text-base lg:text-lg', + // Container queries (Tailwind 4) + '@sm:grid-cols-1 @md:grid-cols-2 @lg:grid-cols-3', + // Fluid typography + 'text-[clamp(0.875rem,2.5vw,1.125rem)]', + // Modern CSS functions + 'gap-[max(1rem,3vw)]', + 'p-[clamp(1rem,5vw,2rem)]' +); +``` + +## Animation Patterns (CSS-Only) + +### Tailwind CSS Animations +```typescript +// ✅ CSS-only animations (no external animation libraries) +const animationClasses = cn( + // Base transition with better performance + 'transition-all duration-200 ease-out', + // Modern transform patterns + 'hover:scale-[1.02] active:scale-[0.98]', + // Modern focus styles + 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2', + // Data state animations for Radix components + 'data-[state=open]:animate-in data-[state=closed]:animate-out', + 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0' +); +``` + +## Accessibility Patterns + +### Keyboard Navigation (Based on Your Components) +```typescript +// ✅ Accessibility patterns for your existing components +export function useKeyboardNavigation() { + const handleKeyDown = React.useCallback((event: React.KeyboardEvent) => { + switch (event.key) { + case 'Enter': + case ' ': + event.preventDefault(); + // Handle activation + break; + case 'Escape': + // Handle escape + break; + case 'ArrowDown': + event.preventDefault(); + // Focus next item + break; + case 'ArrowUp': + event.preventDefault(); + // Focus previous item + break; + } + }, []); + + return { handleKeyDown }; +} +``` + +## Performance Optimization + +### Optimized Class Patterns +```typescript +// ✅ Optimized for your component library +const optimizedClasses = { + // Use CSS custom properties for dynamic values + dynamic: 'bg-[--dynamic-color] text-[--dynamic-text]', + + // Prefer CSS Grid for complex layouts + layout: 'grid grid-cols-[auto_1fr_auto] gap-4', + + // Use container queries for responsive components + responsive: '@container @sm:grid-cols-2 @md:grid-cols-3', + + // Optimize animations for performance + animation: 'transform-gpu will-change-transform', +}; + +// ✅ Memoized component pattern +export const OptimizedComponent = React.memo(({ children, variant }: ComponentProps) => { + const classes = React.useMemo(() => + cn(componentVariants({ variant })), + [variant] + ); + + return
{children}
; +}); +``` + +Remember: Focus only on the components and patterns that exist in the lambda-curry/forms library. Use Radix UI primitives with Tailwind CSS 4, follow the established component architecture, and ensure accessibility and performance in all implementations. +