refactor: consolidate AGENTS.md and CLAUDE.md files, update tech stac… (#444)
* refactor: consolidate AGENTS.md and CLAUDE.md files, update tech stack and architecture details - Merged content from CLAUDE.md into AGENTS.md for better organization. - Updated tech stack section to reflect the current technologies used, including Next.js, Supabase, and Tailwind CSS. - Enhanced monorepo structure documentation with detailed directory purposes. - Streamlined multi-tenant architecture explanation and essential commands. - Added key patterns for naming conventions and server actions. - Removed outdated agent files related to Playwright and PostgreSQL, ensuring a cleaner codebase. - Bumped version to 2.23.7 to reflect changes.
This commit is contained in:
committed by
GitHub
parent
bebd56238b
commit
cfa137795b
@@ -1,46 +1,43 @@
|
||||
# UI Components & Styling Instructions
|
||||
# UI Components & Styling
|
||||
|
||||
This file contains instructions for working with UI components, styling, and forms.
|
||||
## Skills
|
||||
|
||||
## Core UI Library
|
||||
For forms:
|
||||
- `/react-form-builder` - Forms with validation and server actions
|
||||
|
||||
Import from `packages/ui/src/`:
|
||||
## Import Convention
|
||||
|
||||
Always use `@kit/ui/{component}`:
|
||||
|
||||
```tsx
|
||||
// Shadcn components
|
||||
import { Button } from '@kit/ui/button';
|
||||
import { Card } from '@kit/ui/card';
|
||||
|
||||
// Makerkit components
|
||||
import { If } from '@kit/ui/if';
|
||||
import { ProfileAvatar } from '@kit/ui/profile-avatar';
|
||||
import { toast } from '@kit/ui/sonner';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
```
|
||||
|
||||
NB: imports must follow the convention "@kit/ui/<name>", no matter the folder they're placed in
|
||||
|
||||
## Styling Guidelines
|
||||
|
||||
- Use **Tailwind CSS v4** with semantic classes
|
||||
- Prefer Shadcn-ui classes like `bg-background`, `text-muted-foreground`
|
||||
- Use `cn()` utility from `@kit/ui/utils` for class merging
|
||||
|
||||
```tsx
|
||||
import { toast } from '@kit/ui/sonner';
|
||||
import { cn } from '@kit/ui/utils';
|
||||
|
||||
function MyComponent({ className }) {
|
||||
return (
|
||||
<div className={cn('bg-background text-foreground', className)}>
|
||||
Content
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Conditional Rendering
|
||||
## Styling
|
||||
|
||||
Use the `If` component from `packages/ui/src/makerkit/if.tsx`:
|
||||
- Tailwind CSS v4 with semantic classes
|
||||
- Prefer: `bg-background`, `text-muted-foreground`, `border-border`
|
||||
- Use `cn()` for class merging
|
||||
- Never use hardcoded colors like `bg-white`
|
||||
|
||||
## Key Components
|
||||
|
||||
| Component | Usage |
|
||||
|-----------|-------|
|
||||
| `If` | Conditional rendering |
|
||||
| `Trans` | Internationalization |
|
||||
| `toast` | Notifications |
|
||||
| `Form*` | Form fields |
|
||||
| `Button` | Actions |
|
||||
| `Card` | Content containers |
|
||||
| `Alert` | Error/info messages |
|
||||
|
||||
## Conditional Rendering
|
||||
|
||||
```tsx
|
||||
import { If } from '@kit/ui/if';
|
||||
@@ -48,257 +45,27 @@ import { If } from '@kit/ui/if';
|
||||
<If condition={isLoading} fallback={<Content />}>
|
||||
<Spinner />
|
||||
</If>
|
||||
|
||||
// With type inference
|
||||
<If condition={error}>
|
||||
{(err) => <ErrorMessage error={err} />}
|
||||
</If>
|
||||
```
|
||||
|
||||
### Testing Attributes
|
||||
|
||||
```tsx
|
||||
<button data-test="submit-button">Submit</button>
|
||||
<div data-test="user-profile" data-user-id={user.id}>Profile</div>
|
||||
```
|
||||
|
||||
## Forms with React Hook Form & Zod
|
||||
|
||||
```typescript
|
||||
// 1. Schema in separate file
|
||||
export const CreateNoteSchema = z.object({
|
||||
title: z.string().min(1),
|
||||
content: z.string().min(1),
|
||||
});
|
||||
|
||||
// 2. Client component with form
|
||||
'use client';
|
||||
const form = useForm({
|
||||
resolver: zodResolver(CreateNoteSchema),
|
||||
});
|
||||
|
||||
const onSubmit = (data) => {
|
||||
startTransition(async () => {
|
||||
await toast.promise(createNoteAction(data), {
|
||||
loading: 'Creating...',
|
||||
success: 'Created!',
|
||||
error: 'Failed!',
|
||||
}).unwrap();
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
### Form Examples
|
||||
|
||||
- Contact form: `apps/web/app/(marketing)/contact/_components/contact-form.tsx`
|
||||
- Verify OTP form: `packages/otp/src/components/verify-otp-form.tsx`
|
||||
|
||||
### Guidelines
|
||||
|
||||
- Place Zod resolver outside so it can be reused with Server Actions
|
||||
- Never add generics to `useForm`, use Zod resolver to infer types instead
|
||||
- Never use `watch()` instead use hook `useWatch`
|
||||
- Add `FormDescription` (optionally) and always add `FormMessage` to display errors
|
||||
|
||||
## Internationalization
|
||||
|
||||
Always use `Trans` component from `packages/ui/src/makerkit/trans.tsx`:
|
||||
|
||||
```tsx
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
<Trans
|
||||
i18nKey="user:welcomeMessage"
|
||||
values={{ name: user.name }}
|
||||
/>
|
||||
|
||||
// With HTML elements
|
||||
<Trans
|
||||
i18nKey="terms:agreement"
|
||||
components={{
|
||||
TermsLink: <a href="/terms" className="underline" />,
|
||||
}}
|
||||
/>
|
||||
<Trans i18nKey="namespace:key" values={{ name }} />
|
||||
```
|
||||
|
||||
## Toast Notifications
|
||||
## Testing Attributes
|
||||
|
||||
Use the `toast` utility from `@kit/ui/sonner`:
|
||||
Always add `data-test` for E2E:
|
||||
|
||||
```tsx
|
||||
import { toast } from '@kit/ui/sonner';
|
||||
|
||||
// Simple toast
|
||||
toast.success('Success message');
|
||||
toast.error('Error message');
|
||||
|
||||
// Promise-based toast
|
||||
await toast.promise(asyncFunction(), {
|
||||
loading: 'Processing...',
|
||||
success: 'Done!',
|
||||
error: 'Failed!',
|
||||
});
|
||||
<button data-test="submit-button">Submit</button>
|
||||
```
|
||||
|
||||
## Common Component Patterns
|
||||
## Form Guidelines
|
||||
|
||||
### Loading States
|
||||
|
||||
```tsx
|
||||
import { Spinner } from '@kit/ui/spinner';
|
||||
|
||||
<If condition={isLoading} fallback={<Content />}>
|
||||
<Spinner className="h-4 w-4" />
|
||||
</If>
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
```tsx
|
||||
import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert';
|
||||
import { ExclamationTriangleIcon } from '@radix-ui/react-icons';
|
||||
|
||||
<If condition={Boolean(error)}>
|
||||
<Alert variant="destructive">
|
||||
<ExclamationTriangleIcon className="h-4 w-4" />
|
||||
<AlertTitle>Error</AlertTitle>
|
||||
<AlertDescription>{error}</AlertDescription>
|
||||
</Alert>
|
||||
</If>
|
||||
```
|
||||
|
||||
### Button Patterns
|
||||
|
||||
```tsx
|
||||
import { Button } from '@kit/ui/button';
|
||||
|
||||
// Loading button
|
||||
<Button disabled={isPending}>
|
||||
{isPending ? (
|
||||
<>
|
||||
<Spinner className="mr-2 h-4 w-4" />
|
||||
Loading...
|
||||
</>
|
||||
) : (
|
||||
'Submit'
|
||||
)}
|
||||
</Button>
|
||||
|
||||
// Variants
|
||||
<Button variant="default">Default</Button>
|
||||
<Button variant="destructive">Delete</Button>
|
||||
<Button variant="outline">Cancel</Button>
|
||||
<Button variant="ghost">Ghost</Button>
|
||||
```
|
||||
|
||||
### Card Layouts
|
||||
|
||||
```tsx
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@kit/ui/card';
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Card Title</CardTitle>
|
||||
<CardDescription>Card description</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
Card content goes here
|
||||
</CardContent>
|
||||
</Card>
|
||||
```
|
||||
|
||||
## Form Components
|
||||
|
||||
### Input Fields
|
||||
|
||||
```tsx
|
||||
import { Input } from '@kit/ui/input';
|
||||
import { Label } from '@kit/ui/label';
|
||||
import { FormField, FormItem, FormLabel, FormControl, FormMessage } from '@kit/ui/form';
|
||||
|
||||
<FormField
|
||||
name="title"
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Title</FormLabel>
|
||||
|
||||
<FormControl>
|
||||
<Input placeholder="Enter title" {...field} />
|
||||
</FormControl>
|
||||
|
||||
<FormDescription>
|
||||
The title of your task
|
||||
</FormDescription>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
```
|
||||
|
||||
### Select Components
|
||||
|
||||
```tsx
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@kit/ui/select';
|
||||
|
||||
<FormField
|
||||
name="category"
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Category</FormLabel>
|
||||
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select category" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
|
||||
<SelectContent>
|
||||
<SelectItem value="option1">Option 1</SelectItem>
|
||||
<SelectItem value="option2">Option 2</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<FormDescription>
|
||||
The category of your task
|
||||
</FormDescription>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
```
|
||||
|
||||
## Accessibility Guidelines
|
||||
|
||||
- Always include proper ARIA labels
|
||||
- Use semantic HTML elements
|
||||
- Ensure proper keyboard navigation
|
||||
|
||||
```tsx
|
||||
<button
|
||||
aria-label="Close modal"
|
||||
aria-describedby="modal-description"
|
||||
onClick={onClose}
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
```
|
||||
|
||||
## Dark Mode Support
|
||||
|
||||
The UI components automatically support dark mode through CSS variables. Use semantic color classes:
|
||||
|
||||
```tsx
|
||||
// Good - semantic colors
|
||||
<div className="bg-background text-foreground border-border">
|
||||
<p className="text-muted-foreground">Secondary text</p>
|
||||
</div>
|
||||
|
||||
// Avoid - hardcoded colors
|
||||
<div className="bg-white text-black border-gray-200">
|
||||
<p className="text-gray-500">Secondary text</p>
|
||||
</div>
|
||||
```
|
||||
- Use `react-hook-form` with `zodResolver`
|
||||
- Never add generics to `useForm`
|
||||
- Use `useWatch` instead of `watch()`
|
||||
- Always include `FormMessage` for errors
|
||||
|
||||
Reference in New Issue
Block a user