@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,117 @@
1
+ # Checkbox Component Recipe
2
+
3
+ ## Structure
4
+ - Clickable box with check indicator
5
+ - Optional label alongside
6
+ - Support indeterminate state
7
+ - Group wrapper for multiple checkboxes
8
+
9
+ ## Tailwind Classes
10
+
11
+ ### Checkbox Box
12
+ ```
13
+ peer h-4 w-4 shrink-0 {tokens.radius} border border-border
14
+ ring-offset-background
15
+ focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2
16
+ disabled:cursor-not-allowed disabled:opacity-50
17
+ data-[state=checked]:bg-primary data-[state=checked]:border-primary data-[state=checked]:text-primary-foreground
18
+ data-[state=indeterminate]:bg-primary data-[state=indeterminate]:border-primary
19
+ ```
20
+
21
+ ### Check Icon
22
+ ```
23
+ h-3.5 w-3.5 text-current
24
+ ```
25
+
26
+ ### Label
27
+ ```
28
+ text-sm font-medium leading-none text-foreground
29
+ peer-disabled:cursor-not-allowed peer-disabled:opacity-70
30
+ ```
31
+
32
+ ### Wrapper (checkbox + label)
33
+ ```
34
+ flex items-center space-x-2
35
+ ```
36
+
37
+ ### Description (optional)
38
+ ```
39
+ text-sm text-muted-foreground
40
+ ```
41
+
42
+ ## Props Interface
43
+ ```typescript
44
+ interface CheckboxProps {
45
+ checked?: boolean | 'indeterminate'
46
+ onCheckedChange?: (checked: boolean | 'indeterminate') => void
47
+ disabled?: boolean
48
+ id?: string
49
+ name?: string
50
+ }
51
+
52
+ interface CheckboxWithLabelProps extends CheckboxProps {
53
+ label: string
54
+ description?: string
55
+ }
56
+ ```
57
+
58
+ ## Do
59
+ - Use `peer` class for label styling based on checkbox state
60
+ - Include focus ring for accessibility
61
+ - Support indeterminate state (for "select all" patterns)
62
+ - Use semantic color tokens
63
+ - Animate check appearance
64
+
65
+ ## Don't
66
+ - Hardcode colors
67
+ - Use actual `<input type="checkbox">` without styling wrapper
68
+ - Forget disabled state styling
69
+ - Skip focus indicators
70
+
71
+ ## Example
72
+ ```tsx
73
+ import * as CheckboxPrimitive from '@radix-ui/react-checkbox'
74
+ import { Check, Minus } from 'lucide-react'
75
+ import { cn } from '@/lib/utils'
76
+
77
+ const Checkbox = ({ className, ...props }) => (
78
+ <CheckboxPrimitive.Root
79
+ className={cn(
80
+ 'peer h-4 w-4 shrink-0 rounded border border-border',
81
+ 'ring-offset-background',
82
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2',
83
+ 'disabled:cursor-not-allowed disabled:opacity-50',
84
+ 'data-[state=checked]:bg-primary data-[state=checked]:border-primary data-[state=checked]:text-primary-foreground',
85
+ 'data-[state=indeterminate]:bg-primary data-[state=indeterminate]:border-primary data-[state=indeterminate]:text-primary-foreground',
86
+ className
87
+ )}
88
+ {...props}
89
+ >
90
+ <CheckboxPrimitive.Indicator className="flex items-center justify-center text-current">
91
+ {props.checked === 'indeterminate' ? (
92
+ <Minus className="h-3.5 w-3.5" />
93
+ ) : (
94
+ <Check className="h-3.5 w-3.5" />
95
+ )}
96
+ </CheckboxPrimitive.Indicator>
97
+ </CheckboxPrimitive.Root>
98
+ )
99
+
100
+ // With label wrapper
101
+ const CheckboxWithLabel = ({ label, description, id, ...props }) => (
102
+ <div className="flex items-start space-x-2">
103
+ <Checkbox id={id} {...props} />
104
+ <div className="grid gap-1.5 leading-none">
105
+ <label
106
+ htmlFor={id}
107
+ className="text-sm font-medium leading-none text-foreground peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
108
+ >
109
+ {label}
110
+ </label>
111
+ {description && (
112
+ <p className="text-sm text-muted-foreground">{description}</p>
113
+ )}
114
+ </div>
115
+ </div>
116
+ )
117
+ ```
@@ -0,0 +1,201 @@
1
+ # Collapsible Component Recipe
2
+
3
+ ## Structure
4
+ - Trigger element to toggle
5
+ - Content area that expands/collapses
6
+ - Smooth height animation
7
+ - Optional chevron indicator
8
+
9
+ ## Tailwind Classes
10
+
11
+ ### Root
12
+ ```
13
+ w-full
14
+ ```
15
+
16
+ ### Trigger
17
+ ```
18
+ flex items-center justify-between w-full [&[data-state=open]>svg]:rotate-180
19
+ ```
20
+
21
+ ### Trigger with default styling
22
+ ```
23
+ flex items-center justify-between w-full py-2 font-medium
24
+ transition-all hover:underline
25
+ ```
26
+
27
+ ### Chevron Icon
28
+ ```
29
+ h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200
30
+ ```
31
+
32
+ ### Content
33
+ ```
34
+ overflow-hidden
35
+ data-[state=closed]:animate-collapsible-up
36
+ data-[state=open]:animate-collapsible-down
37
+ ```
38
+
39
+ ### Content Inner (for padding)
40
+ ```
41
+ pt-2 pb-4
42
+ ```
43
+
44
+ ## Animations (add to tailwind.config.js)
45
+ ```js
46
+ keyframes: {
47
+ 'collapsible-down': {
48
+ from: { height: '0' },
49
+ to: { height: 'var(--radix-collapsible-content-height)' },
50
+ },
51
+ 'collapsible-up': {
52
+ from: { height: 'var(--radix-collapsible-content-height)' },
53
+ to: { height: '0' },
54
+ },
55
+ },
56
+ animation: {
57
+ 'collapsible-down': 'collapsible-down 0.2s ease-out',
58
+ 'collapsible-up': 'collapsible-up 0.2s ease-out',
59
+ },
60
+ ```
61
+
62
+ ## Props Interface
63
+ ```typescript
64
+ interface CollapsibleProps {
65
+ open?: boolean
66
+ onOpenChange?: (open: boolean) => void
67
+ defaultOpen?: boolean
68
+ disabled?: boolean
69
+ children: React.ReactNode
70
+ }
71
+
72
+ interface CollapsibleTriggerProps {
73
+ asChild?: boolean
74
+ children: React.ReactNode
75
+ }
76
+
77
+ interface CollapsibleContentProps {
78
+ forceMount?: boolean
79
+ className?: string
80
+ children: React.ReactNode
81
+ }
82
+ ```
83
+
84
+ ## Use Cases
85
+ - FAQ sections
86
+ - Sidebar navigation groups
87
+ - Settings panels
88
+ - Advanced options sections
89
+ - Code snippets with preview
90
+
91
+ ## Do
92
+ - Use Radix Collapsible for accessibility
93
+ - Animate height smoothly
94
+ - Include visual indicator of state (chevron)
95
+ - Support keyboard toggle (Enter/Space)
96
+
97
+ ## Don't
98
+ - Hardcode colors
99
+ - Use abrupt show/hide (animate it)
100
+ - Nest collapsibles too deeply
101
+ - Use for primary content that should always be visible
102
+
103
+ ## Example
104
+ ```tsx
105
+ import * as CollapsiblePrimitive from '@radix-ui/react-collapsible'
106
+ import { ChevronDown } from 'lucide-react'
107
+ import { cn } from '@/lib/utils'
108
+
109
+ const Collapsible = CollapsiblePrimitive.Root
110
+
111
+ const CollapsibleTrigger = CollapsiblePrimitive.Trigger
112
+
113
+ const CollapsibleContent = ({ className, children, ...props }) => (
114
+ <CollapsiblePrimitive.Content
115
+ className={cn(
116
+ 'overflow-hidden',
117
+ 'data-[state=closed]:animate-collapsible-up',
118
+ 'data-[state=open]:animate-collapsible-down',
119
+ className
120
+ )}
121
+ {...props}
122
+ >
123
+ {children}
124
+ </CollapsiblePrimitive.Content>
125
+ )
126
+
127
+ // Simple usage
128
+ const SimpleCollapsible = ({ title, children }) => (
129
+ <Collapsible>
130
+ <CollapsibleTrigger className="flex items-center justify-between w-full py-2 font-medium hover:underline [&[data-state=open]>svg]:rotate-180">
131
+ {title}
132
+ <ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
133
+ </CollapsibleTrigger>
134
+ <CollapsibleContent>
135
+ <div className="pt-2 pb-4">{children}</div>
136
+ </CollapsibleContent>
137
+ </Collapsible>
138
+ )
139
+
140
+ // With border styling
141
+ const CollapsibleCard = ({ title, defaultOpen = false, children }) => (
142
+ <Collapsible defaultOpen={defaultOpen} className="rounded-lg border border-border">
143
+ <CollapsibleTrigger className="flex items-center justify-between w-full p-4 font-medium [&[data-state=open]>svg]:rotate-180">
144
+ {title}
145
+ <ChevronDown className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" />
146
+ </CollapsibleTrigger>
147
+ <CollapsibleContent>
148
+ <div className="px-4 pb-4 pt-0 border-t border-border">
149
+ {children}
150
+ </div>
151
+ </CollapsibleContent>
152
+ </Collapsible>
153
+ )
154
+
155
+ // Sidebar navigation example
156
+ const SidebarGroup = ({ label, items, defaultOpen = true }) => (
157
+ <Collapsible defaultOpen={defaultOpen} className="space-y-2">
158
+ <CollapsibleTrigger className="flex items-center justify-between w-full px-2 py-1.5 text-sm font-semibold text-muted-foreground hover:text-foreground [&[data-state=open]>svg]:rotate-180">
159
+ {label}
160
+ <ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
161
+ </CollapsibleTrigger>
162
+ <CollapsibleContent>
163
+ <div className="space-y-1">
164
+ {items.map((item) => (
165
+ <a
166
+ key={item.href}
167
+ href={item.href}
168
+ className="block rounded-md px-2 py-1.5 text-sm hover:bg-muted"
169
+ >
170
+ {item.label}
171
+ </a>
172
+ ))}
173
+ </div>
174
+ </CollapsibleContent>
175
+ </Collapsible>
176
+ )
177
+
178
+ // Code preview with expand
179
+ const CodePreview = ({ preview, code }) => {
180
+ const [open, setOpen] = useState(false)
181
+
182
+ return (
183
+ <Collapsible open={open} onOpenChange={setOpen}>
184
+ <div className="rounded-lg border border-border">
185
+ <div className="p-4">{preview}</div>
186
+ <div className="border-t border-border">
187
+ <CollapsibleTrigger className="flex items-center justify-center w-full py-2 text-sm text-muted-foreground hover:text-foreground">
188
+ {open ? 'Hide code' : 'Show code'}
189
+ <ChevronDown className={cn('ml-1 h-4 w-4 transition-transform', open && 'rotate-180')} />
190
+ </CollapsibleTrigger>
191
+ </div>
192
+ <CollapsibleContent>
193
+ <pre className="p-4 bg-muted text-sm overflow-x-auto">
194
+ <code>{code}</code>
195
+ </pre>
196
+ </CollapsibleContent>
197
+ </div>
198
+ </Collapsible>
199
+ )
200
+ }
201
+ ```
@@ -0,0 +1,193 @@
1
+ # Combobox Component Recipe
2
+
3
+ ## Structure
4
+ - Text input for searching/filtering
5
+ - Dropdown list of options
6
+ - Support for single and multi-select
7
+ - Keyboard navigation (arrows, enter, escape)
8
+ - Option for creating new items
9
+
10
+ ## Tailwind Classes
11
+
12
+ ### Trigger/Input Container
13
+ ```
14
+ flex h-10 w-full items-center justify-between {tokens.radius} border border-border
15
+ bg-background px-3 py-2 text-sm
16
+ focus-within:outline-none focus-within:ring-2 focus-within:ring-primary focus-within:ring-offset-2
17
+ disabled:cursor-not-allowed disabled:opacity-50
18
+ ```
19
+
20
+ ### Input
21
+ ```
22
+ flex-1 bg-transparent outline-none placeholder:text-muted-foreground
23
+ ```
24
+
25
+ ### Trigger Button
26
+ ```
27
+ ml-2 shrink-0 opacity-50 hover:opacity-100
28
+ ```
29
+
30
+ ### Content (Dropdown)
31
+ ```
32
+ relative z-50 max-h-96 min-w-[8rem] overflow-hidden {tokens.radius}
33
+ border border-border bg-background text-foreground {tokens.shadow}
34
+ ```
35
+
36
+ ### Viewport
37
+ ```
38
+ p-1 max-h-[300px] overflow-y-auto
39
+ ```
40
+
41
+ ### Empty State
42
+ ```
43
+ py-6 text-center text-sm text-muted-foreground
44
+ ```
45
+
46
+ ### Item
47
+ ```
48
+ relative flex cursor-pointer select-none items-center {tokens.radius} py-1.5 pl-2 pr-8
49
+ text-sm outline-none
50
+ data-[highlighted]:bg-muted data-[highlighted]:text-foreground
51
+ data-[disabled]:pointer-events-none data-[disabled]:opacity-50
52
+ ```
53
+
54
+ ### Item Indicator (check)
55
+ ```
56
+ absolute right-2 flex h-3.5 w-3.5 items-center justify-center
57
+ ```
58
+
59
+ ### Group
60
+ ```
61
+ overflow-hidden p-1
62
+ ```
63
+
64
+ ### Group Label
65
+ ```
66
+ px-2 py-1.5 text-xs font-medium text-muted-foreground
67
+ ```
68
+
69
+ ### Separator
70
+ ```
71
+ -mx-1 my-1 h-px bg-border
72
+ ```
73
+
74
+ ## Props Interface
75
+ ```typescript
76
+ interface ComboboxProps {
77
+ value?: string | string[]
78
+ onValueChange?: (value: string | string[]) => void
79
+ options: { value: string; label: string; disabled?: boolean }[]
80
+ placeholder?: string
81
+ searchPlaceholder?: string
82
+ emptyText?: string
83
+ disabled?: boolean
84
+ multiple?: boolean
85
+ allowCreate?: boolean
86
+ onCreate?: (value: string) => void
87
+ }
88
+ ```
89
+
90
+ ## Do
91
+ - Use Radix Combobox or cmdk for accessibility
92
+ - Support type-ahead filtering
93
+ - Clear search on selection (single) or keep (multi)
94
+ - Show loading state while filtering
95
+
96
+ ## Don't
97
+ - Hardcode colors
98
+ - Forget empty state message
99
+ - Skip keyboard navigation
100
+ - Load all options upfront for large lists (use virtualization)
101
+
102
+ ## Example
103
+ ```tsx
104
+ import { useState } from 'react'
105
+ import { Command } from 'cmdk'
106
+ import { Check, ChevronsUpDown } from 'lucide-react'
107
+ import { cn } from '@/lib/utils'
108
+ import { Popover, PopoverContent, PopoverTrigger } from './Popover'
109
+ import { Button } from './Button'
110
+
111
+ const Combobox = ({
112
+ options,
113
+ value,
114
+ onValueChange,
115
+ placeholder = 'Select option...',
116
+ searchPlaceholder = 'Search...',
117
+ emptyText = 'No results found.',
118
+ }) => {
119
+ const [open, setOpen] = useState(false)
120
+ const [search, setSearch] = useState('')
121
+
122
+ const selected = options.find(opt => opt.value === value)
123
+
124
+ return (
125
+ <Popover open={open} onOpenChange={setOpen}>
126
+ <PopoverTrigger asChild>
127
+ <Button
128
+ variant="outline"
129
+ role="combobox"
130
+ aria-expanded={open}
131
+ className="w-full justify-between"
132
+ >
133
+ {selected?.label ?? placeholder}
134
+ <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
135
+ </Button>
136
+ </PopoverTrigger>
137
+ <PopoverContent className="w-full p-0">
138
+ <Command>
139
+ <Command.Input
140
+ placeholder={searchPlaceholder}
141
+ value={search}
142
+ onValueChange={setSearch}
143
+ className="h-9 border-b border-border px-3 outline-none"
144
+ />
145
+ <Command.Empty className="py-6 text-center text-sm text-muted-foreground">
146
+ {emptyText}
147
+ </Command.Empty>
148
+ <Command.List className="max-h-[300px] overflow-y-auto p-1">
149
+ {options.map(option => (
150
+ <Command.Item
151
+ key={option.value}
152
+ value={option.value}
153
+ onSelect={() => {
154
+ onValueChange?.(option.value)
155
+ setOpen(false)
156
+ }}
157
+ className={cn(
158
+ 'relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm',
159
+ 'data-[highlighted]:bg-muted'
160
+ )}
161
+ >
162
+ {option.label}
163
+ {value === option.value && (
164
+ <Check className="absolute right-2 h-4 w-4" />
165
+ )}
166
+ </Command.Item>
167
+ ))}
168
+ </Command.List>
169
+ </Command>
170
+ </PopoverContent>
171
+ </Popover>
172
+ )
173
+ }
174
+
175
+ // Multi-select variant
176
+ const MultiCombobox = ({ options, value = [], onValueChange, ...props }) => {
177
+ const toggleOption = (optionValue) => {
178
+ const newValue = value.includes(optionValue)
179
+ ? value.filter(v => v !== optionValue)
180
+ : [...value, optionValue]
181
+ onValueChange?.(newValue)
182
+ }
183
+
184
+ return (
185
+ <Combobox
186
+ {...props}
187
+ options={options}
188
+ value={value}
189
+ onValueChange={toggleOption}
190
+ />
191
+ )
192
+ }
193
+ ```