@fr0mpy/component-system 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/bin/cli.js +283 -0
  2. package/index.js +12 -0
  3. package/package.json +45 -0
  4. package/templates/commands/component-harness.md +116 -0
  5. package/templates/commands/setup-styling.md +111 -0
  6. package/templates/component-recipes/accordion.md +153 -0
  7. package/templates/component-recipes/alert.md +145 -0
  8. package/templates/component-recipes/avatar.md +165 -0
  9. package/templates/component-recipes/badge.md +126 -0
  10. package/templates/component-recipes/breadcrumb.md +220 -0
  11. package/templates/component-recipes/button.md +90 -0
  12. package/templates/component-recipes/card.md +130 -0
  13. package/templates/component-recipes/carousel.md +277 -0
  14. package/templates/component-recipes/checkbox.md +117 -0
  15. package/templates/component-recipes/collapsible.md +201 -0
  16. package/templates/component-recipes/combobox.md +193 -0
  17. package/templates/component-recipes/context-menu.md +254 -0
  18. package/templates/component-recipes/dialog.md +193 -0
  19. package/templates/component-recipes/drawer.md +196 -0
  20. package/templates/component-recipes/dropdown-menu.md +263 -0
  21. package/templates/component-recipes/hover-card.md +230 -0
  22. package/templates/component-recipes/input.md +113 -0
  23. package/templates/component-recipes/label.md +259 -0
  24. package/templates/component-recipes/modal.md +155 -0
  25. package/templates/component-recipes/navigation-menu.md +310 -0
  26. package/templates/component-recipes/pagination.md +223 -0
  27. package/templates/component-recipes/popover.md +156 -0
  28. package/templates/component-recipes/progress.md +185 -0
  29. package/templates/component-recipes/radio.md +148 -0
  30. package/templates/component-recipes/select.md +154 -0
  31. package/templates/component-recipes/separator.md +124 -0
  32. package/templates/component-recipes/skeleton.md +186 -0
  33. package/templates/component-recipes/slider.md +114 -0
  34. package/templates/component-recipes/spinner.md +225 -0
  35. package/templates/component-recipes/switch.md +100 -0
  36. package/templates/component-recipes/table.md +161 -0
  37. package/templates/component-recipes/tabs.md +145 -0
  38. package/templates/component-recipes/textarea.md +234 -0
  39. package/templates/component-recipes/toast.md +209 -0
  40. package/templates/component-recipes/toggle-group.md +216 -0
  41. package/templates/component-recipes/tooltip.md +115 -0
  42. package/templates/hooks/triggers.d/styling.json +23 -0
  43. package/templates/skills/styling.md +173 -0
@@ -0,0 +1,153 @@
1
+ # Accordion Component Recipe
2
+
3
+ ## Structure
4
+ - Root container with multiple items
5
+ - Each item has trigger (header) and content
6
+ - Chevron indicator that rotates on open
7
+ - Support single or multiple items open
8
+
9
+ ## Tailwind Classes
10
+
11
+ ### Item
12
+ ```
13
+ border-b border-border
14
+ ```
15
+
16
+ ### Trigger
17
+ ```
18
+ flex flex-1 items-center justify-between py-4 text-sm font-medium
19
+ transition-all hover:underline
20
+ [&[data-state=open]>svg]:rotate-180
21
+ ```
22
+
23
+ ### Chevron Icon
24
+ ```
25
+ h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200
26
+ ```
27
+
28
+ ### Content
29
+ ```
30
+ overflow-hidden text-sm
31
+ data-[state=closed]:animate-accordion-up
32
+ data-[state=open]:animate-accordion-down
33
+ ```
34
+
35
+ ### Content Inner
36
+ ```
37
+ pb-4 pt-0
38
+ ```
39
+
40
+ ## Animations (add to tailwind.config.js)
41
+ ```js
42
+ keyframes: {
43
+ 'accordion-down': {
44
+ from: { height: '0' },
45
+ to: { height: 'var(--radix-accordion-content-height)' },
46
+ },
47
+ 'accordion-up': {
48
+ from: { height: 'var(--radix-accordion-content-height)' },
49
+ to: { height: '0' },
50
+ },
51
+ },
52
+ animation: {
53
+ 'accordion-down': 'accordion-down 0.2s ease-out',
54
+ 'accordion-up': 'accordion-up 0.2s ease-out',
55
+ },
56
+ ```
57
+
58
+ ## Props Interface
59
+ ```typescript
60
+ interface AccordionProps {
61
+ type?: 'single' | 'multiple'
62
+ defaultValue?: string | string[]
63
+ value?: string | string[]
64
+ onValueChange?: (value: string | string[]) => void
65
+ collapsible?: boolean // only for type="single"
66
+ children: React.ReactNode
67
+ }
68
+
69
+ interface AccordionItemProps {
70
+ value: string
71
+ disabled?: boolean
72
+ children: React.ReactNode
73
+ }
74
+
75
+ interface AccordionTriggerProps {
76
+ className?: string
77
+ children: React.ReactNode
78
+ }
79
+
80
+ interface AccordionContentProps {
81
+ className?: string
82
+ children: React.ReactNode
83
+ }
84
+ ```
85
+
86
+ ## Do
87
+ - Use Radix Accordion for accessibility
88
+ - Animate height changes smoothly
89
+ - Rotate chevron on open
90
+ - Support both single and multiple modes
91
+
92
+ ## Don't
93
+ - Hardcode colors
94
+ - Skip keyboard accessibility
95
+ - Use abrupt open/close (animate it)
96
+ - Forget border between items
97
+
98
+ ## Example
99
+ ```tsx
100
+ import * as AccordionPrimitive from '@radix-ui/react-accordion'
101
+ import { ChevronDown } from 'lucide-react'
102
+ import { cn } from '@/lib/utils'
103
+
104
+ const Accordion = AccordionPrimitive.Root
105
+
106
+ const AccordionItem = ({ className, ...props }) => (
107
+ <AccordionPrimitive.Item
108
+ className={cn('border-b border-border', className)}
109
+ {...props}
110
+ />
111
+ )
112
+
113
+ const AccordionTrigger = ({ className, children, ...props }) => (
114
+ <AccordionPrimitive.Header className="flex">
115
+ <AccordionPrimitive.Trigger
116
+ className={cn(
117
+ 'flex flex-1 items-center justify-between py-4 text-sm font-medium',
118
+ 'transition-all hover:underline [&[data-state=open]>svg]:rotate-180',
119
+ className
120
+ )}
121
+ {...props}
122
+ >
123
+ {children}
124
+ <ChevronDown className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" />
125
+ </AccordionPrimitive.Trigger>
126
+ </AccordionPrimitive.Header>
127
+ )
128
+
129
+ const AccordionContent = ({ className, children, ...props }) => (
130
+ <AccordionPrimitive.Content
131
+ className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
132
+ {...props}
133
+ >
134
+ <div className={cn('pb-4 pt-0', className)}>{children}</div>
135
+ </AccordionPrimitive.Content>
136
+ )
137
+
138
+ // Usage
139
+ <Accordion type="single" collapsible>
140
+ <AccordionItem value="item-1">
141
+ <AccordionTrigger>Is it accessible?</AccordionTrigger>
142
+ <AccordionContent>
143
+ Yes. It adheres to the WAI-ARIA design pattern.
144
+ </AccordionContent>
145
+ </AccordionItem>
146
+ <AccordionItem value="item-2">
147
+ <AccordionTrigger>Is it styled?</AccordionTrigger>
148
+ <AccordionContent>
149
+ Yes. It comes with default styles that match your design system.
150
+ </AccordionContent>
151
+ </AccordionItem>
152
+ </Accordion>
153
+ ```
@@ -0,0 +1,145 @@
1
+ # Alert Component Recipe
2
+
3
+ ## Structure
4
+ - Container with icon, title, and description
5
+ - Support variants: info, success, warning, error/destructive
6
+ - Optional dismiss button
7
+ - Optional action buttons
8
+
9
+ ## Tailwind Classes
10
+
11
+ ### Container
12
+ ```
13
+ relative w-full {tokens.radius} border p-4
14
+ [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:h-4 [&>svg]:w-4
15
+ [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-11
16
+ ```
17
+
18
+ ### Variants
19
+ ```
20
+ default: bg-background border-border text-foreground [&>svg]:text-foreground
21
+ info: bg-blue-500/10 border-blue-500/20 text-blue-600 [&>svg]:text-blue-600
22
+ success: bg-green-500/10 border-green-500/20 text-green-600 [&>svg]:text-green-600
23
+ warning: bg-yellow-500/10 border-yellow-500/20 text-yellow-600 [&>svg]:text-yellow-600
24
+ destructive: bg-destructive/10 border-destructive/20 text-destructive [&>svg]:text-destructive
25
+ ```
26
+
27
+ ### Title
28
+ ```
29
+ mb-1 font-medium leading-none tracking-tight
30
+ ```
31
+
32
+ ### Description
33
+ ```
34
+ text-sm [&_p]:leading-relaxed
35
+ ```
36
+
37
+ ### Dismiss Button
38
+ ```
39
+ absolute right-2 top-2 {tokens.radius} p-1 opacity-70
40
+ hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-primary
41
+ ```
42
+
43
+ ## Props Interface
44
+ ```typescript
45
+ interface AlertProps {
46
+ variant?: 'default' | 'info' | 'success' | 'warning' | 'destructive'
47
+ icon?: React.ReactNode
48
+ title?: string
49
+ onDismiss?: () => void
50
+ children: React.ReactNode
51
+ }
52
+
53
+ interface AlertTitleProps {
54
+ className?: string
55
+ children: React.ReactNode
56
+ }
57
+
58
+ interface AlertDescriptionProps {
59
+ className?: string
60
+ children: React.ReactNode
61
+ }
62
+ ```
63
+
64
+ ## Icons per Variant
65
+ ```
66
+ info: <Info /> or <AlertCircle />
67
+ success: <CheckCircle />
68
+ warning: <AlertTriangle />
69
+ destructive: <XCircle /> or <AlertOctagon />
70
+ default: <Info /> or custom
71
+ ```
72
+
73
+ ## Do
74
+ - Include appropriate icon for each variant
75
+ - Use semantic colors from tokens where possible
76
+ - Support composable pattern (Alert.Title, Alert.Description)
77
+ - Add dismiss functionality when needed
78
+
79
+ ## Don't
80
+ - Hardcode colors
81
+ - Make alerts too visually heavy
82
+ - Forget to position icon properly
83
+ - Skip the semantic meaning of variants
84
+
85
+ ## Example
86
+ ```tsx
87
+ import { cn } from '@/lib/utils'
88
+ import { AlertCircle, CheckCircle, AlertTriangle, XCircle, X, Info } from 'lucide-react'
89
+
90
+ const alertVariants = {
91
+ default: 'bg-background border-border text-foreground',
92
+ info: 'bg-blue-500/10 border-blue-500/20 text-blue-600 [&>svg]:text-blue-600',
93
+ success: 'bg-green-500/10 border-green-500/20 text-green-600 [&>svg]:text-green-600',
94
+ warning: 'bg-yellow-500/10 border-yellow-500/20 text-yellow-600 [&>svg]:text-yellow-600',
95
+ destructive: 'bg-destructive/10 border-destructive/20 text-destructive [&>svg]:text-destructive',
96
+ }
97
+
98
+ const alertIcons = {
99
+ default: Info,
100
+ info: AlertCircle,
101
+ success: CheckCircle,
102
+ warning: AlertTriangle,
103
+ destructive: XCircle,
104
+ }
105
+
106
+ const Alert = ({ variant = 'default', title, onDismiss, className, children, ...props }) => {
107
+ const Icon = alertIcons[variant]
108
+
109
+ return (
110
+ <div
111
+ role="alert"
112
+ className={cn(
113
+ 'relative w-full rounded-lg border p-4',
114
+ '[&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:h-4 [&>svg]:w-4',
115
+ '[&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-11',
116
+ alertVariants[variant],
117
+ className
118
+ )}
119
+ {...props}
120
+ >
121
+ <Icon className="h-4 w-4" />
122
+ <div>
123
+ {title && <AlertTitle>{title}</AlertTitle>}
124
+ <AlertDescription>{children}</AlertDescription>
125
+ </div>
126
+ {onDismiss && (
127
+ <button
128
+ onClick={onDismiss}
129
+ className="absolute right-2 top-2 rounded p-1 opacity-70 hover:opacity-100"
130
+ >
131
+ <X className="h-4 w-4" />
132
+ </button>
133
+ )}
134
+ </div>
135
+ )
136
+ }
137
+
138
+ const AlertTitle = ({ className, ...props }) => (
139
+ <h5 className={cn('mb-1 font-medium leading-none tracking-tight', className)} {...props} />
140
+ )
141
+
142
+ const AlertDescription = ({ className, ...props }) => (
143
+ <div className={cn('text-sm [&_p]:leading-relaxed', className)} {...props} />
144
+ )
145
+ ```
@@ -0,0 +1,165 @@
1
+ # Avatar Component Recipe
2
+
3
+ ## Structure
4
+ - Circular container for image or fallback
5
+ - Support image, initials fallback, or icon fallback
6
+ - Multiple sizes
7
+ - Optional status indicator (online/offline)
8
+ - Avatar group for stacking
9
+
10
+ ## Tailwind Classes
11
+
12
+ ### Container
13
+ ```
14
+ relative flex shrink-0 overflow-hidden rounded-full
15
+ ```
16
+
17
+ ### Sizes
18
+ ```
19
+ xs: h-6 w-6 text-xs
20
+ sm: h-8 w-8 text-xs
21
+ md: h-10 w-10 text-sm (default)
22
+ lg: h-12 w-12 text-base
23
+ xl: h-16 w-16 text-lg
24
+ 2xl: h-24 w-24 text-xl
25
+ ```
26
+
27
+ ### Image
28
+ ```
29
+ aspect-square h-full w-full object-cover
30
+ ```
31
+
32
+ ### Fallback (initials or icon)
33
+ ```
34
+ flex h-full w-full items-center justify-center rounded-full
35
+ bg-muted text-muted-foreground font-medium
36
+ ```
37
+
38
+ ### Status Indicator
39
+ ```
40
+ absolute bottom-0 right-0 h-3 w-3 rounded-full border-2 border-background
41
+ online: bg-green-500
42
+ offline: bg-gray-400
43
+ busy: bg-red-500
44
+ away: bg-yellow-500
45
+ ```
46
+
47
+ ### Avatar Group
48
+ ```
49
+ flex -space-x-2
50
+ [&>*]:ring-2 [&>*]:ring-background
51
+ ```
52
+
53
+ ## Props Interface
54
+ ```typescript
55
+ interface AvatarProps {
56
+ src?: string
57
+ alt?: string
58
+ fallback?: string | React.ReactNode
59
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
60
+ status?: 'online' | 'offline' | 'busy' | 'away'
61
+ }
62
+
63
+ interface AvatarGroupProps {
64
+ max?: number
65
+ children: React.ReactNode
66
+ }
67
+ ```
68
+
69
+ ## Do
70
+ - Use Radix Avatar for image loading states
71
+ - Show fallback immediately while image loads
72
+ - Support both initials and icon fallbacks
73
+ - Use ring for avatar group overlap effect
74
+
75
+ ## Don't
76
+ - Hardcode colors
77
+ - Forget alt text for images
78
+ - Use non-square aspect ratios
79
+ - Skip loading state handling
80
+
81
+ ## Example
82
+ ```tsx
83
+ import * as AvatarPrimitive from '@radix-ui/react-avatar'
84
+ import { cn } from '@/lib/utils'
85
+ import { User } from 'lucide-react'
86
+
87
+ const avatarSizes = {
88
+ xs: 'h-6 w-6 text-xs',
89
+ sm: 'h-8 w-8 text-xs',
90
+ md: 'h-10 w-10 text-sm',
91
+ lg: 'h-12 w-12 text-base',
92
+ xl: 'h-16 w-16 text-lg',
93
+ '2xl': 'h-24 w-24 text-xl',
94
+ }
95
+
96
+ const statusColors = {
97
+ online: 'bg-green-500',
98
+ offline: 'bg-gray-400',
99
+ busy: 'bg-red-500',
100
+ away: 'bg-yellow-500',
101
+ }
102
+
103
+ const Avatar = ({ src, alt, fallback, size = 'md', status, className }) => (
104
+ <div className="relative inline-block">
105
+ <AvatarPrimitive.Root
106
+ className={cn(
107
+ 'relative flex shrink-0 overflow-hidden rounded-full',
108
+ avatarSizes[size],
109
+ className
110
+ )}
111
+ >
112
+ <AvatarPrimitive.Image
113
+ src={src}
114
+ alt={alt}
115
+ className="aspect-square h-full w-full object-cover"
116
+ />
117
+ <AvatarPrimitive.Fallback
118
+ className="flex h-full w-full items-center justify-center rounded-full bg-muted text-muted-foreground font-medium"
119
+ >
120
+ {fallback || <User className="h-1/2 w-1/2" />}
121
+ </AvatarPrimitive.Fallback>
122
+ </AvatarPrimitive.Root>
123
+ {status && (
124
+ <span
125
+ className={cn(
126
+ 'absolute bottom-0 right-0 h-3 w-3 rounded-full border-2 border-background',
127
+ statusColors[status]
128
+ )}
129
+ />
130
+ )}
131
+ </div>
132
+ )
133
+
134
+ // Helper to get initials from name
135
+ const getInitials = (name: string) => {
136
+ return name
137
+ .split(' ')
138
+ .map(n => n[0])
139
+ .join('')
140
+ .toUpperCase()
141
+ .slice(0, 2)
142
+ }
143
+
144
+ // Avatar group for stacking
145
+ const AvatarGroup = ({ max = 4, children }) => {
146
+ const avatars = React.Children.toArray(children)
147
+ const shown = avatars.slice(0, max)
148
+ const remaining = avatars.length - max
149
+
150
+ return (
151
+ <div className="flex -space-x-2">
152
+ {shown.map((avatar, i) => (
153
+ <div key={i} className="ring-2 ring-background rounded-full">
154
+ {avatar}
155
+ </div>
156
+ ))}
157
+ {remaining > 0 && (
158
+ <div className="flex h-10 w-10 items-center justify-center rounded-full bg-muted text-muted-foreground text-sm font-medium ring-2 ring-background">
159
+ +{remaining}
160
+ </div>
161
+ )}
162
+ </div>
163
+ )
164
+ }
165
+ ```
@@ -0,0 +1,126 @@
1
+ # Badge Component Recipe
2
+
3
+ ## Structure
4
+ - Inline `<span>` or `<div>` element
5
+ - Support variants for different contexts
6
+ - Optional icon or close button
7
+ - Compact sizing
8
+
9
+ ## Tailwind Classes
10
+
11
+ ### Base
12
+ ```
13
+ inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium
14
+ transition-colors
15
+ ```
16
+
17
+ ### Variants
18
+ ```
19
+ default: bg-primary text-primary-foreground
20
+ secondary: bg-secondary text-secondary-foreground
21
+ outline: border border-border text-foreground bg-transparent
22
+ destructive: bg-destructive text-destructive-foreground
23
+ success: bg-green-500/10 text-green-600 border border-green-500/20
24
+ warning: bg-yellow-500/10 text-yellow-600 border border-yellow-500/20
25
+ ```
26
+
27
+ ### Sizes
28
+ ```
29
+ sm: px-2 py-0.5 text-xs
30
+ md: px-2.5 py-0.5 text-xs (default)
31
+ lg: px-3 py-1 text-sm
32
+ ```
33
+
34
+ ### With Icon
35
+ ```
36
+ gap-1
37
+ Icon: h-3 w-3
38
+ ```
39
+
40
+ ### With Close Button
41
+ ```
42
+ pr-1
43
+ Close button: ml-1 h-3 w-3 hover:bg-black/10 rounded-full
44
+ ```
45
+
46
+ ### Interactive (clickable)
47
+ ```
48
+ cursor-pointer hover:opacity-80
49
+ ```
50
+
51
+ ## Props Interface
52
+ ```typescript
53
+ interface BadgeProps {
54
+ variant?: 'default' | 'secondary' | 'outline' | 'destructive' | 'success' | 'warning'
55
+ size?: 'sm' | 'md' | 'lg'
56
+ icon?: React.ReactNode
57
+ onClose?: () => void
58
+ children: React.ReactNode
59
+ }
60
+ ```
61
+
62
+ ## Do
63
+ - Use `rounded-full` for pill shape (or `{tokens.radius}` for consistency)
64
+ - Keep text short (1-2 words typically)
65
+ - Use semantic variants (success, warning, destructive)
66
+ - Support icon placement
67
+
68
+ ## Don't
69
+ - Make badges too large
70
+ - Use for long text content
71
+ - Hardcode colors
72
+ - Forget hover state for interactive badges
73
+
74
+ ## Example
75
+ ```tsx
76
+ import { cn } from '@/lib/utils'
77
+ import { X } from 'lucide-react'
78
+
79
+ const badgeVariants = {
80
+ default: 'bg-primary text-primary-foreground',
81
+ secondary: 'bg-secondary text-secondary-foreground',
82
+ outline: 'border border-border text-foreground bg-transparent',
83
+ destructive: 'bg-destructive text-destructive-foreground',
84
+ success: 'bg-green-500/10 text-green-600 border border-green-500/20',
85
+ warning: 'bg-yellow-500/10 text-yellow-600 border border-yellow-500/20',
86
+ }
87
+
88
+ const badgeSizes = {
89
+ sm: 'px-2 py-0.5 text-xs',
90
+ md: 'px-2.5 py-0.5 text-xs',
91
+ lg: 'px-3 py-1 text-sm',
92
+ }
93
+
94
+ const Badge = ({
95
+ variant = 'default',
96
+ size = 'md',
97
+ icon,
98
+ onClose,
99
+ className,
100
+ children,
101
+ ...props
102
+ }) => (
103
+ <span
104
+ className={cn(
105
+ 'inline-flex items-center rounded-full font-medium transition-colors',
106
+ badgeVariants[variant],
107
+ badgeSizes[size],
108
+ icon && 'gap-1',
109
+ onClose && 'pr-1',
110
+ className
111
+ )}
112
+ {...props}
113
+ >
114
+ {icon && <span className="h-3 w-3">{icon}</span>}
115
+ {children}
116
+ {onClose && (
117
+ <button
118
+ onClick={onClose}
119
+ className="ml-1 rounded-full p-0.5 hover:bg-black/10"
120
+ >
121
+ <X className="h-3 w-3" />
122
+ </button>
123
+ )}
124
+ </span>
125
+ )
126
+ ```