omgkit 2.1.1 → 2.3.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.
- package/package.json +1 -1
- package/plugin/skills/databases/mongodb/SKILL.md +81 -28
- package/plugin/skills/databases/prisma/SKILL.md +87 -32
- package/plugin/skills/databases/redis/SKILL.md +80 -27
- package/plugin/skills/devops/aws/SKILL.md +80 -26
- package/plugin/skills/devops/github-actions/SKILL.md +84 -32
- package/plugin/skills/devops/kubernetes/SKILL.md +94 -32
- package/plugin/skills/devops/performance-profiling/SKILL.md +59 -863
- package/plugin/skills/frameworks/django/SKILL.md +158 -24
- package/plugin/skills/frameworks/express/SKILL.md +153 -33
- package/plugin/skills/frameworks/fastapi/SKILL.md +153 -34
- package/plugin/skills/frameworks/laravel/SKILL.md +146 -33
- package/plugin/skills/frameworks/nestjs/SKILL.md +137 -25
- package/plugin/skills/frameworks/rails/SKILL.md +594 -28
- package/plugin/skills/frameworks/react/SKILL.md +94 -962
- package/plugin/skills/frameworks/spring/SKILL.md +528 -35
- package/plugin/skills/frameworks/vue/SKILL.md +147 -25
- package/plugin/skills/frontend/accessibility/SKILL.md +145 -36
- package/plugin/skills/frontend/frontend-design/SKILL.md +114 -29
- package/plugin/skills/frontend/responsive/SKILL.md +131 -28
- package/plugin/skills/frontend/shadcn-ui/SKILL.md +133 -43
- package/plugin/skills/frontend/tailwindcss/SKILL.md +105 -37
- package/plugin/skills/frontend/threejs/SKILL.md +110 -35
- package/plugin/skills/languages/javascript/SKILL.md +195 -34
- package/plugin/skills/methodology/brainstorming/SKILL.md +98 -30
- package/plugin/skills/methodology/defense-in-depth/SKILL.md +83 -37
- package/plugin/skills/methodology/dispatching-parallel-agents/SKILL.md +92 -31
- package/plugin/skills/methodology/executing-plans/SKILL.md +117 -28
- package/plugin/skills/methodology/finishing-development-branch/SKILL.md +111 -32
- package/plugin/skills/methodology/problem-solving/SKILL.md +65 -311
- package/plugin/skills/methodology/receiving-code-review/SKILL.md +76 -27
- package/plugin/skills/methodology/requesting-code-review/SKILL.md +93 -22
- package/plugin/skills/methodology/root-cause-tracing/SKILL.md +75 -40
- package/plugin/skills/methodology/sequential-thinking/SKILL.md +75 -224
- package/plugin/skills/methodology/systematic-debugging/SKILL.md +81 -35
- package/plugin/skills/methodology/test-driven-development/SKILL.md +120 -26
- package/plugin/skills/methodology/testing-anti-patterns/SKILL.md +88 -35
- package/plugin/skills/methodology/token-optimization/SKILL.md +73 -34
- package/plugin/skills/methodology/verification-before-completion/SKILL.md +128 -28
- package/plugin/skills/methodology/writing-plans/SKILL.md +105 -20
- package/plugin/skills/omega/omega-architecture/SKILL.md +178 -40
- package/plugin/skills/omega/omega-coding/SKILL.md +247 -41
- package/plugin/skills/omega/omega-sprint/SKILL.md +208 -46
- package/plugin/skills/omega/omega-testing/SKILL.md +253 -42
- package/plugin/skills/omega/omega-thinking/SKILL.md +263 -51
- package/plugin/skills/security/better-auth/SKILL.md +83 -34
- package/plugin/skills/security/oauth/SKILL.md +118 -35
- package/plugin/skills/security/owasp/SKILL.md +112 -35
- package/plugin/skills/testing/playwright/SKILL.md +141 -38
- package/plugin/skills/testing/pytest/SKILL.md +137 -38
- package/plugin/skills/testing/vitest/SKILL.md +124 -39
- package/plugin/skills/tools/document-processing/SKILL.md +111 -838
- package/plugin/skills/tools/image-processing/SKILL.md +126 -659
- package/plugin/skills/tools/mcp-development/SKILL.md +85 -758
- package/plugin/skills/tools/media-processing/SKILL.md +118 -735
- package/plugin/stdrules/SKILL_STANDARDS.md +490 -0
|
@@ -1,46 +1,149 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: responsive
|
|
3
|
-
description:
|
|
2
|
+
name: building-responsive-layouts
|
|
3
|
+
description: Claude builds responsive web layouts with mobile-first CSS, fluid typography, and container queries. Use when creating adaptive UIs that work across all device sizes.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Responsive
|
|
6
|
+
# Building Responsive Layouts
|
|
7
7
|
|
|
8
|
-
##
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
## Quick Start
|
|
9
|
+
|
|
10
|
+
```tsx
|
|
11
|
+
// Responsive grid with auto-fit
|
|
12
|
+
export function ResponsiveGrid({ children, minWidth = '300px' }: GridProps) {
|
|
13
|
+
return (
|
|
14
|
+
<div style={{
|
|
15
|
+
display: 'grid',
|
|
16
|
+
gridTemplateColumns: `repeat(auto-fit, minmax(min(${minWidth}, 100%), 1fr))`,
|
|
17
|
+
gap: '1.5rem'
|
|
18
|
+
}}>
|
|
19
|
+
{children}
|
|
20
|
+
</div>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
12
23
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
24
|
+
// With Tailwind
|
|
25
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 md:gap-6">
|
|
26
|
+
{items.map(item => <Card key={item.id} {...item} />)}
|
|
27
|
+
</div>
|
|
17
28
|
```
|
|
18
29
|
|
|
19
|
-
##
|
|
30
|
+
## Features
|
|
20
31
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
32
|
+
| Feature | Description | Guide |
|
|
33
|
+
|---------|-------------|-------|
|
|
34
|
+
| Mobile-First Breakpoints | sm(640), md(768), lg(1024), xl(1280), 2xl(1536) | `ref/breakpoints.md` |
|
|
35
|
+
| Fluid Typography | `clamp()` for responsive font sizes | `ref/fluid-type.md` |
|
|
36
|
+
| Container Queries | Component-level responsive design | `ref/container-queries.md` |
|
|
37
|
+
| Responsive Images | srcset, sizes, and art direction | `ref/images.md` |
|
|
38
|
+
| Touch-Friendly | 44px minimum targets, hover vs touch handling | `ref/touch.md` |
|
|
39
|
+
| Safe Areas | Handle notched devices and dynamic viewports | `ref/safe-areas.md` |
|
|
25
40
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
41
|
+
## Common Patterns
|
|
42
|
+
|
|
43
|
+
### useMediaQuery Hook
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
export function useMediaQuery(query: string): boolean {
|
|
47
|
+
const [matches, setMatches] = useState(false);
|
|
48
|
+
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
const media = window.matchMedia(query);
|
|
51
|
+
setMatches(media.matches);
|
|
52
|
+
const listener = (e: MediaQueryListEvent) => setMatches(e.matches);
|
|
53
|
+
media.addEventListener('change', listener);
|
|
54
|
+
return () => media.removeEventListener('change', listener);
|
|
55
|
+
}, [query]);
|
|
56
|
+
|
|
57
|
+
return matches;
|
|
32
58
|
}
|
|
59
|
+
|
|
60
|
+
// Usage
|
|
61
|
+
const isMobile = useMediaQuery('(max-width: 639px)');
|
|
62
|
+
const prefersReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)');
|
|
33
63
|
```
|
|
34
64
|
|
|
35
|
-
### Container
|
|
65
|
+
### Container Query Component
|
|
66
|
+
|
|
36
67
|
```css
|
|
68
|
+
.card-container {
|
|
69
|
+
container-type: inline-size;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.card {
|
|
73
|
+
display: flex;
|
|
74
|
+
flex-direction: column;
|
|
75
|
+
}
|
|
76
|
+
|
|
37
77
|
@container (min-width: 400px) {
|
|
38
|
-
.card {
|
|
78
|
+
.card {
|
|
79
|
+
flex-direction: row;
|
|
80
|
+
gap: 1rem;
|
|
81
|
+
}
|
|
82
|
+
.card-image { width: 40%; }
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
export function ResponsiveCard({ image, title, content }: CardProps) {
|
|
88
|
+
return (
|
|
89
|
+
<div className="card-container">
|
|
90
|
+
<article className="card">
|
|
91
|
+
<img src={image} alt="" className="card-image" />
|
|
92
|
+
<div className="card-content">
|
|
93
|
+
<h3>{title}</h3>
|
|
94
|
+
<p>{content}</p>
|
|
95
|
+
</div>
|
|
96
|
+
</article>
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Responsive Navigation
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
export function ResponsiveNav() {
|
|
106
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<nav className="relative">
|
|
110
|
+
<div className="flex justify-between items-center h-16 px-4">
|
|
111
|
+
<Logo />
|
|
112
|
+
{/* Desktop nav */}
|
|
113
|
+
<div className="hidden md:flex items-center space-x-8">
|
|
114
|
+
<NavLink href="/">Home</NavLink>
|
|
115
|
+
<NavLink href="/about">About</NavLink>
|
|
116
|
+
</div>
|
|
117
|
+
{/* Mobile menu button */}
|
|
118
|
+
<button
|
|
119
|
+
className="md:hidden p-2 min-h-[44px] min-w-[44px]"
|
|
120
|
+
onClick={() => setIsOpen(!isOpen)}
|
|
121
|
+
aria-expanded={isOpen}
|
|
122
|
+
>
|
|
123
|
+
<span className="sr-only">{isOpen ? 'Close' : 'Open'} menu</span>
|
|
124
|
+
{isOpen ? <XIcon /> : <MenuIcon />}
|
|
125
|
+
</button>
|
|
126
|
+
</div>
|
|
127
|
+
{/* Mobile nav */}
|
|
128
|
+
{isOpen && (
|
|
129
|
+
<div className="md:hidden px-2 pb-3 space-y-1">
|
|
130
|
+
<MobileNavLink href="/" onClick={() => setIsOpen(false)}>Home</MobileNavLink>
|
|
131
|
+
<MobileNavLink href="/about" onClick={() => setIsOpen(false)}>About</MobileNavLink>
|
|
132
|
+
</div>
|
|
133
|
+
)}
|
|
134
|
+
</nav>
|
|
135
|
+
);
|
|
39
136
|
}
|
|
40
137
|
```
|
|
41
138
|
|
|
42
139
|
## Best Practices
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
-
|
|
140
|
+
|
|
141
|
+
| Do | Avoid |
|
|
142
|
+
|----|-------|
|
|
143
|
+
| Start with mobile-first CSS | Hiding essential content on mobile |
|
|
144
|
+
| Use relative units (rem, %, vw) | Fixed pixel widths |
|
|
145
|
+
| Test on real devices | Relying only on hover states |
|
|
146
|
+
| Use 44px minimum touch targets | Small touch targets on mobile |
|
|
147
|
+
| Consider reduced motion preferences | Ignoring landscape orientation |
|
|
148
|
+
| Use CSS logical properties (inline, block) | Device-specific breakpoints |
|
|
149
|
+
| Handle safe-area-inset for notched devices | Assuming mouse input |
|
|
@@ -1,58 +1,148 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: shadcn
|
|
3
|
-
description: shadcn/ui components. Use
|
|
2
|
+
name: building-with-shadcn
|
|
3
|
+
description: Claude builds accessible React UIs using shadcn/ui components with Radix primitives and React Hook Form integration. Use when creating forms, dialogs, or composable UI systems.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# shadcn/ui
|
|
6
|
+
# Building with shadcn/ui
|
|
7
|
+
|
|
8
|
+
## Quick Start
|
|
7
9
|
|
|
8
|
-
## Installation
|
|
9
10
|
```bash
|
|
11
|
+
# Initialize and add components
|
|
10
12
|
npx shadcn-ui@latest init
|
|
11
|
-
npx shadcn-ui@latest add button
|
|
13
|
+
npx shadcn-ui@latest add button card form input dialog
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { Button } from "@/components/ui/button";
|
|
18
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
19
|
+
|
|
20
|
+
export function Example() {
|
|
21
|
+
return (
|
|
22
|
+
<Card>
|
|
23
|
+
<CardHeader>
|
|
24
|
+
<CardTitle>Welcome</CardTitle>
|
|
25
|
+
</CardHeader>
|
|
26
|
+
<CardContent className="flex gap-4">
|
|
27
|
+
<Button>Primary</Button>
|
|
28
|
+
<Button variant="outline">Outline</Button>
|
|
29
|
+
</CardContent>
|
|
30
|
+
</Card>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
|
|
37
|
+
| Feature | Description | Guide |
|
|
38
|
+
|---------|-------------|-------|
|
|
39
|
+
| Button Variants | default, secondary, destructive, outline, ghost, link | `ref/button.md` |
|
|
40
|
+
| Form Integration | React Hook Form + Zod validation pattern | `ref/forms.md` |
|
|
41
|
+
| Dialog/Sheet | Modal dialogs and slide-out panels | `ref/dialogs.md` |
|
|
42
|
+
| Data Display | Table, Tabs, Accordion components | `ref/data-display.md` |
|
|
43
|
+
| Navigation | DropdownMenu, Command palette, NavigationMenu | `ref/navigation.md` |
|
|
44
|
+
| Feedback | Toast notifications with useToast hook | `ref/toast.md` |
|
|
45
|
+
|
|
46
|
+
## Common Patterns
|
|
47
|
+
|
|
48
|
+
### Form with Validation
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
const formSchema = z.object({
|
|
52
|
+
email: z.string().email("Invalid email"),
|
|
53
|
+
name: z.string().min(2, "Name must be at least 2 characters"),
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
export function ProfileForm() {
|
|
57
|
+
const form = useForm<z.infer<typeof formSchema>>({
|
|
58
|
+
resolver: zodResolver(formSchema),
|
|
59
|
+
defaultValues: { email: "", name: "" },
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<Form {...form}>
|
|
64
|
+
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
|
65
|
+
<FormField control={form.control} name="email" render={({ field }) => (
|
|
66
|
+
<FormItem>
|
|
67
|
+
<FormLabel>Email</FormLabel>
|
|
68
|
+
<FormControl><Input {...field} /></FormControl>
|
|
69
|
+
<FormMessage />
|
|
70
|
+
</FormItem>
|
|
71
|
+
)} />
|
|
72
|
+
<FormField control={form.control} name="name" render={({ field }) => (
|
|
73
|
+
<FormItem>
|
|
74
|
+
<FormLabel>Name</FormLabel>
|
|
75
|
+
<FormControl><Input {...field} /></FormControl>
|
|
76
|
+
<FormMessage />
|
|
77
|
+
</FormItem>
|
|
78
|
+
)} />
|
|
79
|
+
<Button type="submit" disabled={form.formState.isSubmitting}>
|
|
80
|
+
{form.formState.isSubmitting ? "Saving..." : "Save"}
|
|
81
|
+
</Button>
|
|
82
|
+
</form>
|
|
83
|
+
</Form>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
12
86
|
```
|
|
13
87
|
|
|
14
|
-
|
|
88
|
+
### Dialog with Form
|
|
89
|
+
|
|
15
90
|
```tsx
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
<Button variant="
|
|
21
|
-
|
|
22
|
-
<
|
|
23
|
-
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
</
|
|
91
|
+
export function EditDialog({ onSave }: { onSave: (data: Data) => void }) {
|
|
92
|
+
return (
|
|
93
|
+
<Dialog>
|
|
94
|
+
<DialogTrigger asChild>
|
|
95
|
+
<Button variant="outline">Edit Profile</Button>
|
|
96
|
+
</DialogTrigger>
|
|
97
|
+
<DialogContent>
|
|
98
|
+
<DialogHeader>
|
|
99
|
+
<DialogTitle>Edit Profile</DialogTitle>
|
|
100
|
+
<DialogDescription>Update your profile information.</DialogDescription>
|
|
101
|
+
</DialogHeader>
|
|
102
|
+
<div className="grid gap-4 py-4">
|
|
103
|
+
<div className="grid grid-cols-4 items-center gap-4">
|
|
104
|
+
<Label htmlFor="name" className="text-right">Name</Label>
|
|
105
|
+
<Input id="name" className="col-span-3" />
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
<DialogFooter>
|
|
109
|
+
<DialogClose asChild><Button variant="outline">Cancel</Button></DialogClose>
|
|
110
|
+
<Button onClick={() => onSave(data)}>Save</Button>
|
|
111
|
+
</DialogFooter>
|
|
112
|
+
</DialogContent>
|
|
113
|
+
</Dialog>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
32
116
|
```
|
|
33
117
|
|
|
34
|
-
|
|
118
|
+
### Toast Notifications
|
|
119
|
+
|
|
35
120
|
```tsx
|
|
36
|
-
import {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
</Form>
|
|
121
|
+
import { useToast } from "@/components/ui/use-toast";
|
|
122
|
+
|
|
123
|
+
export function SaveButton() {
|
|
124
|
+
const { toast } = useToast();
|
|
125
|
+
|
|
126
|
+
const handleSave = async () => {
|
|
127
|
+
try {
|
|
128
|
+
await saveData();
|
|
129
|
+
toast({ title: "Success", description: "Changes saved." });
|
|
130
|
+
} catch {
|
|
131
|
+
toast({ variant: "destructive", title: "Error", description: "Failed to save." });
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
return <Button onClick={handleSave}>Save</Button>;
|
|
136
|
+
}
|
|
53
137
|
```
|
|
54
138
|
|
|
55
139
|
## Best Practices
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
140
|
+
|
|
141
|
+
| Do | Avoid |
|
|
142
|
+
|----|-------|
|
|
143
|
+
| Install only components you need | Modifying generated component files directly |
|
|
144
|
+
| Use `cn()` utility for class merging | Skipping form validation |
|
|
145
|
+
| Extend components with composition | Overriding styles without good reason |
|
|
146
|
+
| Follow React Hook Form patterns | Using inline styles |
|
|
147
|
+
| Use TypeScript for type safety | Skipping loading and error states |
|
|
148
|
+
| Test component accessibility | Ignoring keyboard navigation |
|
|
@@ -1,52 +1,120 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
3
|
-
description: Tailwind CSS
|
|
2
|
+
name: styling-with-tailwind
|
|
3
|
+
description: Claude styles UIs with Tailwind CSS utility classes, responsive design, and dark mode support. Use when creating component styles, theming systems, or responsive layouts.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Tailwind CSS
|
|
6
|
+
# Styling with Tailwind CSS
|
|
7
7
|
|
|
8
|
-
##
|
|
9
|
-
```html
|
|
10
|
-
<!-- Spacing -->
|
|
11
|
-
<div class="p-4 m-2 space-y-4">
|
|
8
|
+
## Quick Start
|
|
12
9
|
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
```tsx
|
|
11
|
+
// Button with variants using utility classes
|
|
12
|
+
export function Button({ variant = 'primary', size = 'md', children }: ButtonProps) {
|
|
13
|
+
const base = 'inline-flex items-center justify-center font-medium rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50';
|
|
14
|
+
const variants = {
|
|
15
|
+
primary: 'bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500',
|
|
16
|
+
outline: 'border-2 border-blue-600 text-blue-600 hover:bg-blue-50 focus:ring-blue-500',
|
|
17
|
+
ghost: 'text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-800',
|
|
18
|
+
};
|
|
19
|
+
const sizes = { sm: 'px-3 py-1.5 text-sm', md: 'px-4 py-2', lg: 'px-6 py-3 text-lg' };
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
return (
|
|
22
|
+
<button className={`${base} ${variants[variant]} ${sizes[size]}`}>
|
|
23
|
+
{children}
|
|
24
|
+
</button>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
21
27
|
```
|
|
22
28
|
|
|
23
|
-
##
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
## Features
|
|
30
|
+
|
|
31
|
+
| Feature | Description | Guide |
|
|
32
|
+
|---------|-------------|-------|
|
|
33
|
+
| Configuration | Extend colors, fonts, spacing, animations | `ref/config.md` |
|
|
34
|
+
| Responsive Design | Mobile-first breakpoints: sm, md, lg, xl, 2xl | `ref/responsive.md` |
|
|
35
|
+
| Dark Mode | Class-based dark mode with `dark:` prefix | `ref/dark-mode.md` |
|
|
36
|
+
| Custom Animations | Define keyframes and animation utilities | `ref/animations.md` |
|
|
37
|
+
| CSS Variables | Theme with CSS custom properties | `ref/theming.md` |
|
|
38
|
+
| Plugins | @tailwindcss/forms, typography, container-queries | `ref/plugins.md` |
|
|
39
|
+
|
|
40
|
+
## Common Patterns
|
|
41
|
+
|
|
42
|
+
### Responsive Card Grid
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
export function CardGrid({ children }: { children: React.ReactNode }) {
|
|
46
|
+
return (
|
|
47
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 md:gap-6">
|
|
48
|
+
{children}
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function Card({ children, hoverable }: { children: React.ReactNode; hoverable?: boolean }) {
|
|
54
|
+
return (
|
|
55
|
+
<div className={cn(
|
|
56
|
+
'bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 p-6',
|
|
57
|
+
hoverable && 'hover:shadow-lg hover:-translate-y-1 transition-all duration-200'
|
|
58
|
+
)}>
|
|
59
|
+
{children}
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
27
63
|
```
|
|
28
64
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
65
|
+
### Dark Mode Toggle
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
export function DarkModeToggle() {
|
|
69
|
+
const [isDark, setIsDark] = useState(false);
|
|
70
|
+
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
const dark = localStorage.theme === 'dark' ||
|
|
73
|
+
(!localStorage.theme && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
|
74
|
+
setIsDark(dark);
|
|
75
|
+
document.documentElement.classList.toggle('dark', dark);
|
|
76
|
+
}, []);
|
|
77
|
+
|
|
78
|
+
const toggle = () => {
|
|
79
|
+
setIsDark(!isDark);
|
|
80
|
+
document.documentElement.classList.toggle('dark', !isDark);
|
|
81
|
+
localStorage.theme = !isDark ? 'dark' : 'light';
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<button onClick={toggle} className="p-2 rounded-lg text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800">
|
|
86
|
+
{isDark ? <SunIcon className="h-5 w-5" /> : <MoonIcon className="h-5 w-5" />}
|
|
87
|
+
</button>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
41
90
|
```
|
|
42
91
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
92
|
+
### Conditional Classes with cn()
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
import { clsx, type ClassValue } from 'clsx';
|
|
96
|
+
import { twMerge } from 'tailwind-merge';
|
|
97
|
+
|
|
98
|
+
export function cn(...inputs: ClassValue[]) {
|
|
99
|
+
return twMerge(clsx(inputs));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Usage
|
|
103
|
+
<div className={cn(
|
|
104
|
+
'px-4 py-2 rounded-lg',
|
|
105
|
+
isActive && 'bg-blue-500 text-white',
|
|
106
|
+
isDisabled && 'opacity-50 cursor-not-allowed',
|
|
107
|
+
className
|
|
108
|
+
)} />
|
|
46
109
|
```
|
|
47
110
|
|
|
48
111
|
## Best Practices
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
112
|
+
|
|
113
|
+
| Do | Avoid |
|
|
114
|
+
|----|-------|
|
|
115
|
+
| Use mobile-first responsive design | Overusing @apply (prefer utilities) |
|
|
116
|
+
| Leverage `cn()` for conditional classes | Hardcoding colors outside theme |
|
|
117
|
+
| Configure content paths for purging | Using arbitrary values excessively |
|
|
118
|
+
| Use CSS variables for theming | Mixing styling paradigms |
|
|
119
|
+
| Group related utilities semantically | Skipping dark mode variants |
|
|
120
|
+
| Use consistent spacing scales | Ignoring responsive testing |
|