@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,259 @@
1
+ # Label Component Recipe
2
+
3
+ ## Structure
4
+ - Text label associated with form controls
5
+ - Uses htmlFor to link to input elements
6
+ - Support for required indicator
7
+ - Support for disabled state
8
+ - Optional description/helper text
9
+
10
+ ## Tailwind Classes
11
+
12
+ ### Base
13
+ ```
14
+ text-sm font-medium leading-none
15
+ peer-disabled:cursor-not-allowed peer-disabled:opacity-70
16
+ ```
17
+
18
+ ### Required Indicator
19
+ ```
20
+ text-destructive ml-1
21
+ ```
22
+
23
+ ### Optional Indicator
24
+ ```
25
+ text-muted-foreground ml-1 font-normal
26
+ ```
27
+
28
+ ### Description
29
+ ```
30
+ text-sm text-muted-foreground
31
+ ```
32
+
33
+ ### With Input Group
34
+ ```
35
+ Container: space-y-2
36
+ Label + Input: flex flex-col gap-1.5
37
+ Inline: flex items-center gap-2
38
+ ```
39
+
40
+ ### Sizes
41
+ ```
42
+ sm: text-xs
43
+ md: text-sm (default)
44
+ lg: text-base
45
+ ```
46
+
47
+ ### Error State
48
+ ```
49
+ text-destructive
50
+ ```
51
+
52
+ ## Props Interface
53
+ ```typescript
54
+ interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {
55
+ required?: boolean
56
+ optional?: boolean
57
+ disabled?: boolean
58
+ error?: boolean
59
+ className?: string
60
+ children: React.ReactNode
61
+ }
62
+
63
+ interface LabelWithDescriptionProps extends LabelProps {
64
+ description?: string
65
+ }
66
+
67
+ interface FormFieldProps {
68
+ label: string
69
+ description?: string
70
+ error?: string
71
+ required?: boolean
72
+ children: React.ReactNode
73
+ }
74
+ ```
75
+
76
+ ## Do
77
+ - Always use htmlFor to associate with input
78
+ - Use Radix Label primitive for accessibility
79
+ - Include required indicator for required fields
80
+ - Keep labels concise and clear
81
+
82
+ ## Don't
83
+ - Hardcode colors
84
+ - Use placeholder as label substitute
85
+ - Hide labels (use sr-only if visually hidden)
86
+ - Forget to link label to input
87
+
88
+ ## Example
89
+ ```tsx
90
+ import * as LabelPrimitive from '@radix-ui/react-label'
91
+ import { cn } from '@/lib/utils'
92
+
93
+ const Label = ({
94
+ className,
95
+ required,
96
+ optional,
97
+ disabled,
98
+ error,
99
+ children,
100
+ ...props
101
+ }: LabelProps) => (
102
+ <LabelPrimitive.Root
103
+ className={cn(
104
+ 'text-sm font-medium leading-none',
105
+ 'peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
106
+ disabled && 'cursor-not-allowed opacity-70',
107
+ error && 'text-destructive',
108
+ className
109
+ )}
110
+ {...props}
111
+ >
112
+ {children}
113
+ {required && <span className="text-destructive ml-1" aria-hidden="true">*</span>}
114
+ {optional && <span className="text-muted-foreground ml-1 font-normal">(optional)</span>}
115
+ </LabelPrimitive.Root>
116
+ )
117
+
118
+ // Label with description
119
+ const LabelWithDescription = ({
120
+ description,
121
+ children,
122
+ ...props
123
+ }: LabelWithDescriptionProps) => (
124
+ <div className="space-y-1">
125
+ <Label {...props}>{children}</Label>
126
+ {description && (
127
+ <p className="text-sm text-muted-foreground">{description}</p>
128
+ )}
129
+ </div>
130
+ )
131
+
132
+ // Complete form field wrapper
133
+ const FormField = ({
134
+ label,
135
+ description,
136
+ error,
137
+ required,
138
+ children,
139
+ className,
140
+ }: FormFieldProps) => {
141
+ const id = React.useId()
142
+
143
+ return (
144
+ <div className={cn('space-y-2', className)}>
145
+ <Label htmlFor={id} required={required} error={!!error}>
146
+ {label}
147
+ </Label>
148
+ {description && !error && (
149
+ <p className="text-sm text-muted-foreground">{description}</p>
150
+ )}
151
+ {React.cloneElement(children as React.ReactElement, { id, 'aria-invalid': !!error })}
152
+ {error && (
153
+ <p className="text-sm text-destructive" role="alert">{error}</p>
154
+ )}
155
+ </div>
156
+ )
157
+ }
158
+
159
+ // Inline label (for checkboxes/radios)
160
+ const InlineLabel = ({ className, children, ...props }: LabelProps) => (
161
+ <Label
162
+ className={cn('font-normal cursor-pointer', className)}
163
+ {...props}
164
+ >
165
+ {children}
166
+ </Label>
167
+ )
168
+
169
+ // Usage examples
170
+
171
+ // Basic label with input
172
+ <div className="space-y-2">
173
+ <Label htmlFor="email">Email address</Label>
174
+ <Input id="email" type="email" placeholder="you@example.com" />
175
+ </div>
176
+
177
+ // Required field
178
+ <div className="space-y-2">
179
+ <Label htmlFor="name" required>Full name</Label>
180
+ <Input id="name" required />
181
+ </div>
182
+
183
+ // Optional field
184
+ <div className="space-y-2">
185
+ <Label htmlFor="phone" optional>Phone number</Label>
186
+ <Input id="phone" type="tel" />
187
+ </div>
188
+
189
+ // With description
190
+ <LabelWithDescription
191
+ htmlFor="password"
192
+ description="Must be at least 8 characters with one number"
193
+ >
194
+ Password
195
+ </LabelWithDescription>
196
+ <Input id="password" type="password" />
197
+
198
+ // With error
199
+ <div className="space-y-2">
200
+ <Label htmlFor="username" error>Username</Label>
201
+ <Input id="username" aria-invalid className="border-destructive" />
202
+ <p className="text-sm text-destructive">Username is already taken</p>
203
+ </div>
204
+
205
+ // Form field wrapper
206
+ <FormField
207
+ label="Email"
208
+ description="We'll never share your email"
209
+ required
210
+ error={errors.email?.message}
211
+ >
212
+ <Input type="email" {...register('email')} />
213
+ </FormField>
214
+
215
+ // Inline with checkbox
216
+ <div className="flex items-center gap-2">
217
+ <Checkbox id="terms" />
218
+ <InlineLabel htmlFor="terms">
219
+ I agree to the <a href="/terms" className="underline">terms and conditions</a>
220
+ </InlineLabel>
221
+ </div>
222
+
223
+ // Inline with switch
224
+ <div className="flex items-center justify-between">
225
+ <Label htmlFor="notifications" className="flex flex-col gap-1">
226
+ <span>Email notifications</span>
227
+ <span className="text-sm font-normal text-muted-foreground">
228
+ Receive emails about your account activity
229
+ </span>
230
+ </Label>
231
+ <Switch id="notifications" />
232
+ </div>
233
+
234
+ // Horizontal layout
235
+ <div className="grid grid-cols-4 items-center gap-4">
236
+ <Label htmlFor="name" className="text-right">Name</Label>
237
+ <Input id="name" className="col-span-3" />
238
+ </div>
239
+ ```
240
+
241
+ ## Accessibility Notes
242
+ ```typescript
243
+ // Always associate label with input
244
+ <Label htmlFor="input-id">Label text</Label>
245
+ <Input id="input-id" />
246
+
247
+ // For screen readers, hidden but accessible
248
+ <Label htmlFor="search" className="sr-only">Search</Label>
249
+ <Input id="search" placeholder="Search..." />
250
+
251
+ // Required fields should have aria-required
252
+ <Label htmlFor="email" required>Email</Label>
253
+ <Input id="email" aria-required="true" required />
254
+
255
+ // Error states need aria-invalid and aria-describedby
256
+ <Label htmlFor="password">Password</Label>
257
+ <Input id="password" aria-invalid="true" aria-describedby="password-error" />
258
+ <p id="password-error" role="alert">Password is too short</p>
259
+ ```
@@ -0,0 +1,155 @@
1
+ # Modal/Dialog Component Recipe
2
+
3
+ ## Structure
4
+ - Overlay backdrop with `<div>` for dimming
5
+ - Container panel with `<div role="dialog">`
6
+ - Optional: Header, Body, Footer sections
7
+ - Close button in header or corner
8
+ - Focus trap and keyboard handling
9
+
10
+ ## Tailwind Classes
11
+
12
+ ### Overlay/Backdrop
13
+ ```
14
+ fixed inset-0 z-50 bg-black/50 backdrop-blur-sm
15
+ data-[state=open]:animate-in data-[state=closed]:animate-out
16
+ data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0
17
+ ```
18
+
19
+ ### Panel/Content
20
+ ```
21
+ fixed left-1/2 top-1/2 z-50 -translate-x-1/2 -translate-y-1/2
22
+ w-full max-w-lg {tokens.radius} border border-border bg-background p-6 {tokens.shadow}
23
+ data-[state=open]:animate-in data-[state=closed]:animate-out
24
+ data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0
25
+ data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95
26
+ data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%]
27
+ data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]
28
+ ```
29
+
30
+ ### Header
31
+ ```
32
+ flex flex-col space-y-1.5 text-center sm:text-left
33
+ ```
34
+
35
+ ### Title
36
+ ```
37
+ {tokens.typography.heading} text-lg text-foreground
38
+ ```
39
+
40
+ ### Description
41
+ ```
42
+ text-sm text-muted-foreground
43
+ ```
44
+
45
+ ### Footer
46
+ ```
47
+ flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 mt-4
48
+ ```
49
+
50
+ ### Close Button
51
+ ```
52
+ absolute right-4 top-4 {tokens.radius} opacity-70 ring-offset-background
53
+ transition-opacity hover:opacity-100
54
+ focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2
55
+ disabled:pointer-events-none
56
+ ```
57
+
58
+ ## Props Interface
59
+ ```typescript
60
+ interface ModalProps {
61
+ open: boolean
62
+ onOpenChange: (open: boolean) => void
63
+ children: React.ReactNode
64
+ }
65
+
66
+ interface ModalContentProps {
67
+ className?: string
68
+ children: React.ReactNode
69
+ }
70
+
71
+ interface ModalHeaderProps {
72
+ className?: string
73
+ children: React.ReactNode
74
+ }
75
+
76
+ interface ModalTitleProps {
77
+ className?: string
78
+ children: React.ReactNode
79
+ }
80
+
81
+ interface ModalDescriptionProps {
82
+ className?: string
83
+ children: React.ReactNode
84
+ }
85
+
86
+ interface ModalFooterProps {
87
+ className?: string
88
+ children: React.ReactNode
89
+ }
90
+ ```
91
+
92
+ ## Accessibility
93
+ - Use `role="dialog"` and `aria-modal="true"`
94
+ - Implement focus trap (focus stays within modal)
95
+ - Close on Escape key
96
+ - Close on backdrop click (optional)
97
+ - Return focus to trigger element on close
98
+
99
+ ## Do
100
+ - Use Radix Dialog or similar for accessibility
101
+ - Add enter/exit animations
102
+ - Use `bg-background` for panel (not hardcoded white)
103
+ - Include close button with icon
104
+ - Trap focus within modal
105
+
106
+ ## Don't
107
+ - Hardcode colors or shadows
108
+ - Forget backdrop blur/dim
109
+ - Skip keyboard handling (Escape to close)
110
+ - Allow body scroll when open
111
+
112
+ ## Example
113
+ ```tsx
114
+ import * as DialogPrimitive from '@radix-ui/react-dialog'
115
+ import { X } from 'lucide-react'
116
+ import { cn } from '@/lib/utils'
117
+
118
+ const Modal = DialogPrimitive.Root
119
+ const ModalTrigger = DialogPrimitive.Trigger
120
+
121
+ const ModalContent = ({ className, children, ...props }) => (
122
+ <DialogPrimitive.Portal>
123
+ <DialogPrimitive.Overlay className="fixed inset-0 z-50 bg-black/50 backdrop-blur-sm" />
124
+ <DialogPrimitive.Content
125
+ className={cn(
126
+ 'fixed left-1/2 top-1/2 z-50 -translate-x-1/2 -translate-y-1/2',
127
+ 'w-full max-w-lg rounded-lg border border-border bg-background p-6 shadow-lg',
128
+ className
129
+ )}
130
+ {...props}
131
+ >
132
+ {children}
133
+ <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 hover:opacity-100">
134
+ <X className="h-4 w-4" />
135
+ </DialogPrimitive.Close>
136
+ </DialogPrimitive.Content>
137
+ </DialogPrimitive.Portal>
138
+ )
139
+
140
+ const ModalHeader = ({ className, ...props }) => (
141
+ <div className={cn('flex flex-col space-y-1.5', className)} {...props} />
142
+ )
143
+
144
+ const ModalTitle = ({ className, ...props }) => (
145
+ <DialogPrimitive.Title className={cn('font-semibold text-lg', className)} {...props} />
146
+ )
147
+
148
+ const ModalDescription = ({ className, ...props }) => (
149
+ <DialogPrimitive.Description className={cn('text-sm text-muted-foreground', className)} {...props} />
150
+ )
151
+
152
+ const ModalFooter = ({ className, ...props }) => (
153
+ <div className={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 mt-4', className)} {...props} />
154
+ )
155
+ ```