oxycode-skills 1.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 (30) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +143 -0
  3. package/index.js +17 -0
  4. package/package.json +49 -0
  5. package/skills/anti-slop/README.md +106 -0
  6. package/skills/anti-slop/SKILL.md +311 -0
  7. package/skills/anti-slop/examples/before-after.md +380 -0
  8. package/skills/anti-slop/references/quality-rubric.md +313 -0
  9. package/skills/anti-slop/references/slop-patterns.md +433 -0
  10. package/skills/component-architect/README.md +186 -0
  11. package/skills/component-architect/SKILL.md +644 -0
  12. package/skills/component-architect/examples/component-patterns.md +569 -0
  13. package/skills/component-architect/references/atomic-design.md +516 -0
  14. package/skills/design-audit/README.md +114 -0
  15. package/skills/design-audit/SKILL.md +305 -0
  16. package/skills/design-audit/examples/audit-report.md +424 -0
  17. package/skills/design-audit/references/scoring-rubric.md +498 -0
  18. package/skills/design-md/README.md +106 -0
  19. package/skills/design-md/SKILL.md +262 -0
  20. package/skills/design-md/examples/bad-design.md +195 -0
  21. package/skills/design-md/examples/good-design.md +250 -0
  22. package/skills/design-md/references/design-md-spec.md +267 -0
  23. package/skills/design-md/scripts/validate.sh +134 -0
  24. package/skills/ui-builder/README.md +169 -0
  25. package/skills/ui-builder/SKILL.md +357 -0
  26. package/skills/ui-builder/examples/dashboard.md +323 -0
  27. package/skills/ui-builder/examples/landing-page.md +396 -0
  28. package/skills/ui-builder/references/component-patterns.md +396 -0
  29. package/skills/ui-builder/references/layout-system.md +423 -0
  30. package/skills/ui-builder/references/polish-checklist.md +221 -0
@@ -0,0 +1,569 @@
1
+ # Component Architecture Patterns
2
+
3
+ ## Pattern 1: Compound Components
4
+
5
+ ### Problem
6
+ Configuration overload with too many props.
7
+
8
+ ### Solution
9
+ Use compound components for complex UI.
10
+
11
+ ### Example: Tabs
12
+
13
+ ```tsx
14
+ // ❌ BAD: Configuration overload
15
+ <Tabs
16
+ items={[
17
+ { label: 'Tab 1', content: 'Content 1' },
18
+ { label: 'Tab 2', content: 'Content 2' },
19
+ ]}
20
+ activeTab={activeTab}
21
+ onChange={setActiveTab}
22
+ variant="underline"
23
+ size="md"
24
+ fullWidth
25
+ />
26
+
27
+ // ✅ GOOD: Compound components
28
+ <Tabs value={activeTab} onChange={setActiveTab}>
29
+ <Tabs.List>
30
+ <Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
31
+ <Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
32
+ </Tabs.List>
33
+ <Tabs.Content value="tab1">Content 1</Tabs.Content>
34
+ <Tabs.Content value="tab2">Content 2</Tabs.Content>
35
+ </Tabs>
36
+ ```
37
+
38
+ ### Implementation
39
+
40
+ ```tsx
41
+ // components/tabs/Tabs.tsx
42
+ import { createContext, useContext, useState } from 'react'
43
+
44
+ interface TabsContextValue {
45
+ value: string
46
+ onChange: (value: string) => void
47
+ }
48
+
49
+ const TabsContext = createContext<TabsContextValue | null>(null)
50
+
51
+ function useTabsContext() {
52
+ const context = useContext(TabsContext)
53
+ if (!context) {
54
+ throw new Error('Tabs components must be used within <Tabs>')
55
+ }
56
+ return context
57
+ }
58
+
59
+ interface TabsProps {
60
+ value: string
61
+ onChange: (value: string) => void
62
+ children: React.ReactNode
63
+ }
64
+
65
+ export function Tabs({ value, onChange, children }: TabsProps) {
66
+ return (
67
+ <TabsContext.Provider value={{ value, onChange }}>
68
+ <div>{children}</div>
69
+ </TabsContext.Provider>
70
+ )
71
+ }
72
+
73
+ Tabs.List = function TabsList({ children }: { children: React.ReactNode }) {
74
+ return (
75
+ <div className="flex border-b border-zinc-200" role="tablist">
76
+ {children}
77
+ </div>
78
+ )
79
+ }
80
+
81
+ Tabs.Trigger = function TabsTrigger({
82
+ value,
83
+ children
84
+ }: {
85
+ value: string
86
+ children: React.ReactNode
87
+ }) {
88
+ const { value: selectedValue, onChange } = useTabsContext()
89
+ const isSelected = selectedValue === value
90
+
91
+ return (
92
+ <button
93
+ role="tab"
94
+ aria-selected={isSelected}
95
+ className={`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${
96
+ isSelected
97
+ ? 'border-zinc-900 text-zinc-900'
98
+ : 'border-transparent text-zinc-500 hover:text-zinc-700'
99
+ }`}
100
+ onClick={() => onChange(value)}
101
+ >
102
+ {children}
103
+ </button>
104
+ )
105
+ }
106
+
107
+ Tabs.Content = function TabsContent({
108
+ value,
109
+ children
110
+ }: {
111
+ value: string
112
+ children: React.ReactNode
113
+ }) {
114
+ const { value: selectedValue } = useTabsContext()
115
+
116
+ if (selectedValue !== value) {
117
+ return null
118
+ }
119
+
120
+ return (
121
+ <div role="tabpanel" className="py-4">
122
+ {children}
123
+ </div>
124
+ )
125
+ }
126
+ ```
127
+
128
+ ---
129
+
130
+ ## Pattern 2: Render Props
131
+
132
+ ### Problem
133
+ Need to customize rendering of list items.
134
+
135
+ ### Solution
136
+ Use render props for flexible rendering.
137
+
138
+ ### Example: DataList
139
+
140
+ ```tsx
141
+ // ❌ BAD: Limited customization
142
+ <DataList items={items} renderItem={renderItem} />
143
+
144
+ // ✅ GOOD: Render props
145
+ <DataList items={items}>
146
+ {(item) => (
147
+ <DataList.Item key={item.id}>
148
+ <DataList.Content>{item.name}</DataList.Content>
149
+ </DataList.Item>
150
+ )}
151
+ </DataList>
152
+ ```
153
+
154
+ ### Implementation
155
+
156
+ ```tsx
157
+ // components/data-list/DataList.tsx
158
+ interface DataListProps<T> {
159
+ items: T[]
160
+ children: (item: T) => React.ReactNode
161
+ }
162
+
163
+ export function DataList<T>({ items, children }: DataListProps<T>) {
164
+ return (
165
+ <div className="divide-y divide-zinc-200">
166
+ {items.map(children)}
167
+ </div>
168
+ )
169
+ }
170
+
171
+ DataList.Item = function DataListItem({ children }: { children: React.ReactNode }) {
172
+ return <div className="py-4">{children}</div>
173
+ }
174
+
175
+ DataList.Content = function DataListContent({ children }: { children: React.ReactNode }) {
176
+ return <div className="text-sm text-zinc-900">{children}</div>
177
+ }
178
+ ```
179
+
180
+ ---
181
+
182
+ ## Pattern 3: Custom Hooks
183
+
184
+ ### Problem
185
+ Logic is duplicated across components.
186
+
187
+ ### Solution
188
+ Extract logic into custom hooks.
189
+
190
+ ### Example: useToggle
191
+
192
+ ```tsx
193
+ // ❌ BAD: Duplicated logic
194
+ function Component1() {
195
+ const [isOpen, setIsOpen] = useState(false)
196
+ const toggle = () => setIsOpen(!isOpen)
197
+ }
198
+
199
+ function Component2() {
200
+ const [isOpen, setIsOpen] = useState(false)
201
+ const toggle = () => setIsOpen(!isOpen)
202
+ }
203
+
204
+ // ✅ GOOD: Custom hook
205
+ function useToggle(initialValue = false) {
206
+ const [value, setValue] = useState(initialValue)
207
+ const toggle = useCallback(() => setValue(v => !v), [])
208
+ const setTrue = useCallback(() => setValue(true), [])
209
+ const setFalse = useCallback(() => setValue(false), [])
210
+
211
+ return { value, toggle, setTrue, setFalse }
212
+ }
213
+
214
+ function Component1() {
215
+ const { value: isOpen, toggle } = useToggle()
216
+ }
217
+
218
+ function Component2() {
219
+ const { value: isOpen, toggle } = useToggle()
220
+ }
221
+ ```
222
+
223
+ ### Example: useMediaQuery
224
+
225
+ ```tsx
226
+ // hooks/useMediaQuery.ts
227
+ import { useState, useEffect } from 'react'
228
+
229
+ export function useMediaQuery(query: string) {
230
+ const [matches, setMatches] = useState(false)
231
+
232
+ useEffect(() => {
233
+ const media = window.matchMedia(query)
234
+ setMatches(media.matches)
235
+
236
+ const listener = (e: MediaQueryListEvent) => setMatches(e.matches)
237
+ media.addEventListener('change', listener)
238
+
239
+ return () => media.removeEventListener('change', listener)
240
+ }, [query])
241
+
242
+ return matches
243
+ }
244
+
245
+ // Usage
246
+ function Component() {
247
+ const isMobile = useMediaQuery('(max-width: 768px)')
248
+
249
+ return isMobile ? <MobileLayout /> : <DesktopLayout />
250
+ }
251
+ ```
252
+
253
+ ---
254
+
255
+ ## Pattern 4: Higher-Order Components
256
+
257
+ ### Problem
258
+ Need to add behavior to multiple components.
259
+
260
+ ### Solution
261
+ Use higher-order components.
262
+
263
+ ### Example: withLoading
264
+
265
+ ```tsx
266
+ // ❌ BAD: Duplicated loading logic
267
+ function Component1({ isLoading, data }) {
268
+ if (isLoading) return <Spinner />
269
+ return <div>{data}</div>
270
+ }
271
+
272
+ function Component2({ isLoading, data }) {
273
+ if (isLoading) return <Spinner />
274
+ return <div>{data}</div>
275
+ }
276
+
277
+ // ✅ GOOD: HOC
278
+ function withLoading<P>(
279
+ WrappedComponent: React.ComponentType<P>
280
+ ) {
281
+ return function WithLoadingComponent({
282
+ isLoading,
283
+ ...props
284
+ }: P & { isLoading: boolean }) {
285
+ if (isLoading) return <Spinner />
286
+ return <WrappedComponent {...(props as P)} />
287
+ }
288
+ }
289
+
290
+ const UserCardWithLoading = withLoading(UserCard)
291
+
292
+ // Usage
293
+ <UserCardWithLoading isLoading={loading} user={user} />
294
+ ```
295
+
296
+ ---
297
+
298
+ ## Pattern 5: Context Providers
299
+
300
+ ### Problem
301
+ Need to share state across many components.
302
+
303
+ ### Solution
304
+ Use context providers.
305
+
306
+ ### Example: ThemeProvider
307
+
308
+ ```tsx
309
+ // ❌ BAD: Prop drilling
310
+ function App() {
311
+ const [theme, setTheme] = useState('light')
312
+ return <Layout theme={theme} setTheme={setTheme} />
313
+ }
314
+
315
+ function Layout({ theme, setTheme }) {
316
+ return <Header theme={theme} setTheme={setTheme} />
317
+ }
318
+
319
+ function Header({ theme, setTheme }) {
320
+ return <ThemeToggle theme={theme} setTheme={setTheme} />
321
+ }
322
+
323
+ // ✅ GOOD: Context provider
324
+ const ThemeContext = createContext<{
325
+ theme: string
326
+ setTheme: (theme: string) => void
327
+ } | null>(null)
328
+
329
+ function ThemeProvider({ children }: { children: React.ReactNode }) {
330
+ const [theme, setTheme] = useState('light')
331
+
332
+ return (
333
+ <ThemeContext.Provider value={{ theme, setTheme }}>
334
+ {children}
335
+ </ThemeContext.Provider>
336
+ )
337
+ }
338
+
339
+ function useTheme() {
340
+ const context = useContext(ThemeContext)
341
+ if (!context) {
342
+ throw new Error('useTheme must be used within ThemeProvider')
343
+ }
344
+ return context
345
+ }
346
+
347
+ // Usage
348
+ function App() {
349
+ return (
350
+ <ThemeProvider>
351
+ <Layout />
352
+ </ThemeProvider>
353
+ )
354
+ }
355
+
356
+ function Header() {
357
+ const { theme, setTheme } = useTheme()
358
+ return <ThemeToggle theme={theme} setTheme={setTheme} />
359
+ }
360
+ ```
361
+
362
+ ---
363
+
364
+ ## Pattern 6: Polymorphic Components
365
+
366
+ ### Problem
367
+ Component should render different elements.
368
+
369
+ ### Solution
370
+ Use polymorphic components with `as` prop.
371
+
372
+ ### Example: Box
373
+
374
+ ```tsx
375
+ // ❌ BAD: Fixed element
376
+ function Box({ children }) {
377
+ return <div className="p-4">{children}</div>
378
+ }
379
+
380
+ // Usage
381
+ <Box as="section">Section</Box> // ❌ Can't do this
382
+
383
+ // ✅ GOOD: Polymorphic
384
+ interface BoxProps {
385
+ as?: React.ElementType
386
+ children: React.ReactNode
387
+ }
388
+
389
+ function Box({ as: Component = 'div', children, ...props }: BoxProps) {
390
+ return <Component className="p-4" {...props}>{children}</Component>
391
+ }
392
+
393
+ // Usage
394
+ <Box as="section">Section</Box> // ✅ Renders <section>
395
+ <Box as="article">Article</Box> // ✅ Renders <article>
396
+ <Box>Div</Box> // ✅ Renders <div>
397
+ ```
398
+
399
+ ---
400
+
401
+ ## Pattern 7: Controlled/Uncontrolled
402
+
403
+ ### Problem
404
+ Component needs to work both ways.
405
+
406
+ ### Solution
407
+ Support both controlled and uncontrolled modes.
408
+
409
+ ### Example: Input
410
+
411
+ ```tsx
412
+ interface InputProps {
413
+ value?: string
414
+ defaultValue?: string
415
+ onChange?: (value: string) => void
416
+ }
417
+
418
+ function Input({ value, defaultValue, onChange, ...props }: InputProps) {
419
+ const [internalValue, setInternalValue] = useState(defaultValue || '')
420
+
421
+ const isControlled = value !== undefined
422
+ const currentValue = isControlled ? value : internalValue
423
+
424
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
425
+ const newValue = e.target.value
426
+ if (!isControlled) {
427
+ setInternalValue(newValue)
428
+ }
429
+ onChange?.(newValue)
430
+ }
431
+
432
+ return <input value={currentValue} onChange={handleChange} {...props} />
433
+ }
434
+
435
+ // Controlled usage
436
+ const [value, setValue] = useState('')
437
+ <Input value={value} onChange={setValue} />
438
+
439
+ // Uncontrolled usage
440
+ <Input defaultValue="initial" />
441
+ ```
442
+
443
+ ---
444
+
445
+ ## Pattern 8: Compound Component with Reducer
446
+
447
+ ### Problem
448
+ Complex state management in compound components.
449
+
450
+ ### Solution
451
+ Use reducer for complex state.
452
+
453
+ ### Example: Accordion
454
+
455
+ ```tsx
456
+ type AccordionAction =
457
+ | { type: 'TOGGLE'; value: string }
458
+ | { type: 'CLOSE_ALL' }
459
+
460
+ interface AccordionState {
461
+ openItems: Set<string>
462
+ }
463
+
464
+ function accordionReducer(state: AccordionState, action: AccordionAction): AccordionState {
465
+ switch (action.type) {
466
+ case 'TOGGLE': {
467
+ const newOpenItems = new Set(state.openItems)
468
+ if (newOpenItems.has(action.value)) {
469
+ newOpenItems.delete(action.value)
470
+ } else {
471
+ newOpenItems.add(action.value)
472
+ }
473
+ return { openItems: newOpenItems }
474
+ }
475
+ case 'CLOSE_ALL':
476
+ return { openItems: new Set() }
477
+ default:
478
+ return state
479
+ }
480
+ }
481
+
482
+ interface AccordionContextValue {
483
+ state: AccordionState
484
+ dispatch: React.Dispatch<AccordionAction>
485
+ }
486
+
487
+ const AccordionContext = createContext<AccordionContextValue | null>(null)
488
+
489
+ function Accordion({ children }: { children: React.ReactNode }) {
490
+ const [state, dispatch] = useReducer(accordionReducer, { openItems: new Set() })
491
+
492
+ return (
493
+ <AccordionContext.Provider value={{ state, dispatch }}>
494
+ <div className="divide-y divide-zinc-200">{children}</div>
495
+ </AccordionContext.Provider>
496
+ )
497
+ }
498
+
499
+ Accordion.Item = function AccordionItem({
500
+ value,
501
+ children
502
+ }: {
503
+ value: string
504
+ children: React.ReactNode
505
+ }) {
506
+ return <div>{children}</div>
507
+ }
508
+
509
+ Accordion.Trigger = function AccordionTrigger({
510
+ value,
511
+ children
512
+ }: {
513
+ value: string
514
+ children: React.ReactNode
515
+ }) {
516
+ const { state, dispatch } = useContext(AccordionContext)!
517
+ const isOpen = state.openItems.has(value)
518
+
519
+ return (
520
+ <button
521
+ className="w-full py-4 text-left flex items-center justify-between"
522
+ onClick={() => dispatch({ type: 'TOGGLE', value })}
523
+ >
524
+ {children}
525
+ <ChevronDown className={`w-5 h-5 transition-transform ${isOpen ? 'rotate-180' : ''}`} />
526
+ </button>
527
+ )
528
+ }
529
+
530
+ Accordion.Content = function AccordionContent({
531
+ value,
532
+ children
533
+ }: {
534
+ value: string
535
+ children: React.ReactNode
536
+ }) {
537
+ const { state } = useContext(AccordionContext)!
538
+ const isOpen = state.openItems.has(value)
539
+
540
+ if (!isOpen) return null
541
+
542
+ return <div className="pb-4">{children}</div>
543
+ }
544
+ ```
545
+
546
+ ---
547
+
548
+ ## Summary
549
+
550
+ ### When to Use Each Pattern
551
+
552
+ | Pattern | Use When |
553
+ |---------|----------|
554
+ | Compound Components | Complex UI with multiple parts |
555
+ | Render Props | Need to customize rendering |
556
+ | Custom Hooks | Extract reusable logic |
557
+ | HOC | Add behavior to multiple components |
558
+ | Context Providers | Share state across many components |
559
+ | Polymorphic | Component should render different elements |
560
+ | Controlled/Uncontrolled | Component needs flexibility |
561
+ | Reducer | Complex state management |
562
+
563
+ ### Best Practices
564
+
565
+ 1. **Start simple** - Don't over-engineer
566
+ 2. **Extract when duplicated** - If you copy, extract
567
+ 3. **Document usage** - Show examples
568
+ 4. **Type everything** - Use TypeScript
569
+ 5. **Test thoroughly** - Unit + integration tests