@adlas/create-app 1.0.47 → 1.0.49
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.
|
@@ -4,55 +4,340 @@
|
|
|
4
4
|
|
|
5
5
|
This document provides comprehensive guidelines for converting Figma designs to code in this project. **You MUST strictly follow the project structure, component patterns, and coding standards defined below.**
|
|
6
6
|
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🔌 Figma MCP Server Integration
|
|
10
|
+
|
|
11
|
+
### Prerequisites
|
|
12
|
+
|
|
13
|
+
Before implementing UI from Figma designs, ensure the following:
|
|
14
|
+
|
|
15
|
+
1. **Claude Desktop App** - You must be using Claude Desktop (not web interface)
|
|
16
|
+
2. **Figma MCP Server** - Installed and configured in Claude Desktop settings
|
|
17
|
+
3. **Figma Access Token** - Valid token with read access to the design file
|
|
18
|
+
4. **File Access** - Permissions to view the specific Figma file
|
|
19
|
+
|
|
20
|
+
### Setup Verification
|
|
21
|
+
|
|
22
|
+
Before starting, verify MCP server is working:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Check if Figma server is connected in Claude Desktop
|
|
26
|
+
# You should see "figma" in available MCP servers
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Figma URL Format
|
|
30
|
+
|
|
31
|
+
Supported Figma URL formats:
|
|
32
|
+
- File URL: `https://www.figma.com/file/{file-key}/{file-name}`
|
|
33
|
+
- Design URL: `https://www.figma.com/design/{file-key}/{file-name}`
|
|
34
|
+
- Specific page: Add `?node-id={page-node-id}` to URL
|
|
35
|
+
|
|
36
|
+
### Working with Figma Designs
|
|
37
|
+
|
|
38
|
+
When you receive a Figma URL:
|
|
39
|
+
|
|
40
|
+
1. **Fetch the design** using Figma MCP server
|
|
41
|
+
2. **Analyze the structure** - Understand pages, frames, components
|
|
42
|
+
3. **Identify patterns** - Find repeated components and design tokens
|
|
43
|
+
4. **Plan implementation** - Map Figma components to HeroUI components
|
|
44
|
+
5. **Extract assets** - Export icons (SVG) and images (SVG/PNG/WebP)
|
|
45
|
+
6. **Implement step by step** - Start with layout, then components, then styling
|
|
46
|
+
|
|
47
|
+
### Critical Workflow
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
1. Get Figma URL from user
|
|
51
|
+
↓
|
|
52
|
+
2. Use Figma MCP to fetch design structure
|
|
53
|
+
↓
|
|
54
|
+
3. Identify all pages and components
|
|
55
|
+
↓
|
|
56
|
+
4. Export icons as SVG → src/assets/icons/
|
|
57
|
+
↓
|
|
58
|
+
5. Run pnpm generate-icons
|
|
59
|
+
↓
|
|
60
|
+
6. Export logos/images → public/images/
|
|
61
|
+
↓
|
|
62
|
+
7. Create centralized image constants (src/config/images.ts)
|
|
63
|
+
↓
|
|
64
|
+
8. Map Figma components to HeroUI components
|
|
65
|
+
↓
|
|
66
|
+
9. Implement pages with proper i18n and navigation
|
|
67
|
+
↓
|
|
68
|
+
10. Test responsive behavior across breakpoints
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Important**: Always use Figma MCP server to fetch design details. Never ask user to manually describe the design.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
7
75
|
## 🎯 Core Principles
|
|
8
76
|
|
|
9
77
|
1. **Use HeroUI components first** - Always prefer HeroUI components. Only use Tailwind CSS for custom styling when HeroUI doesn't provide the component
|
|
10
78
|
2. **Use ONLY existing components** - Never create custom implementations when project components exist
|
|
11
79
|
3. **i18n for ALL text** - ALL labels, placeholders, titles, error messages MUST use translation keys
|
|
12
80
|
4. **Navigation from config** - NEVER hardcode URLs. Always use `NAVIGATION_URLS` from config
|
|
13
|
-
5. **
|
|
14
|
-
6. **
|
|
15
|
-
7. **
|
|
16
|
-
8. **
|
|
81
|
+
5. **Icons workflow** - Export SVG from Figma → Place in `src/assets/icons/` → Run `pnpm generate-icons` → Use generated TSX components
|
|
82
|
+
6. **Logos workflow** - Export SVG/PNG from Figma → Place in `public/images/` → Create centralized constants → Reference via `IMAGES.CATEGORY.NAME`
|
|
83
|
+
7. **Use Tailwind standard classes** - Use closest Tailwind class instead of custom values (e.g., `h-10` not `h-[41px]`)
|
|
84
|
+
8. **Follow project structure** - Place files in correct locations as defined below
|
|
85
|
+
9. **Match coding patterns** - Use the same patterns found in existing code
|
|
86
|
+
10. **Type safety first** - Use TypeScript strictly, no `any` types
|
|
17
87
|
|
|
18
88
|
---
|
|
19
89
|
|
|
20
|
-
## 🎨 Component
|
|
90
|
+
## 🎨 Component Configuration & Customization
|
|
91
|
+
|
|
92
|
+
### CRITICAL: Component Styles Must Be Configured, Not Inline
|
|
93
|
+
|
|
94
|
+
**❌ WRONG - Inline custom styles**:
|
|
95
|
+
```tsx
|
|
96
|
+
// Don't apply component-specific styles inline
|
|
97
|
+
<Button className="h-10 rounded-full bg-[#19ffa3] px-4 text-sm text-black">
|
|
98
|
+
Click Me
|
|
99
|
+
</Button>
|
|
100
|
+
|
|
101
|
+
// Don't use HTML elements when HeroUI component exists
|
|
102
|
+
<button onClick={handler}>Click</button>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**✅ CORRECT - Configure in component wrapper with defaultVariants**:
|
|
106
|
+
```tsx
|
|
107
|
+
// File: src/components/ui/Button.tsx
|
|
108
|
+
import { extendVariants, Button as HeroUIButton } from '@heroui/react';
|
|
109
|
+
|
|
110
|
+
export const Button = extendVariants(HeroUIButton, {
|
|
111
|
+
variants: {
|
|
112
|
+
color: {
|
|
113
|
+
primary: 'bg-primary-600 text-black hover:bg-primary-500',
|
|
114
|
+
secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300',
|
|
115
|
+
},
|
|
116
|
+
size: {
|
|
117
|
+
sm: 'h-8 px-3 text-xs',
|
|
118
|
+
md: 'h-10 px-4 text-sm',
|
|
119
|
+
lg: 'h-12 px-6 text-base',
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
defaultVariants: {
|
|
123
|
+
color: 'primary',
|
|
124
|
+
size: 'md',
|
|
125
|
+
radius: 'full',
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Usage in code - Clean and declarative:
|
|
130
|
+
<Button>Click Me</Button> // Uses defaultVariants
|
|
131
|
+
<Button color="secondary" size="lg">Large Button</Button>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**When to configure vs use inline**:
|
|
135
|
+
- ✅ Configure with defaultVariants: Repeated styles, component variants, brand colors, default radius
|
|
136
|
+
- ✅ Inline: Layout utilities only (`mt-4`, `hidden`, `lg:block`)
|
|
137
|
+
- ❌ Never inline: Component-specific styles (heights, colors, padding, text sizes, border radius)
|
|
21
138
|
|
|
22
|
-
###
|
|
23
|
-
Before creating ANY component, check if HeroUI provides it:
|
|
24
|
-
- **HeroUI Documentation**: https://heroui.com/docs/components
|
|
25
|
-
- **HeroUI Figma Kit**: https://www.figma.com/design/kFGcjHsNKZx7zh2NxEJXYt/HeroUI-Figma-Kit--Community---Community-
|
|
139
|
+
### extendVariants Pattern
|
|
26
140
|
|
|
27
|
-
|
|
141
|
+
Use `extendVariants` from HeroUI to create configured component wrappers:
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
import { extendVariants, Input as HeroUIInput } from '@heroui/react';
|
|
145
|
+
|
|
146
|
+
export const Input = extendVariants(HeroUIInput, {
|
|
147
|
+
variants: {
|
|
148
|
+
variant: {
|
|
149
|
+
flat: 'bg-gray-100 border-none',
|
|
150
|
+
bordered: 'border-2 border-gray-300',
|
|
151
|
+
},
|
|
152
|
+
size: {
|
|
153
|
+
sm: 'h-8 text-xs',
|
|
154
|
+
md: 'h-10 text-sm',
|
|
155
|
+
lg: 'h-12 text-base',
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
defaultVariants: {
|
|
159
|
+
variant: 'flat',
|
|
160
|
+
size: 'md',
|
|
161
|
+
radius: 'lg',
|
|
162
|
+
},
|
|
163
|
+
});
|
|
28
164
|
```
|
|
29
|
-
1. Does HeroUI have this component?
|
|
30
|
-
→ YES: Use HeroUI component (via @/components/ui wrapper if exists)
|
|
31
|
-
→ NO: Go to step 2
|
|
32
165
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
166
|
+
**Benefits**:
|
|
167
|
+
- Clean component usage without repetitive className
|
|
168
|
+
- Consistent styling across the application
|
|
169
|
+
- Easy to maintain and update
|
|
170
|
+
- Type-safe variants
|
|
36
171
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
172
|
+
### Data Must Be Mapped from Constants
|
|
173
|
+
|
|
174
|
+
**❌ WRONG - Hardcoded data**:
|
|
175
|
+
```tsx
|
|
176
|
+
<DropdownMenu>
|
|
177
|
+
<DropdownItem key="option1">Option 1</DropdownItem>
|
|
178
|
+
<DropdownItem key="option2">Option 2</DropdownItem>
|
|
179
|
+
<DropdownItem key="option3">Option 3</DropdownItem>
|
|
180
|
+
</DropdownMenu>
|
|
40
181
|
```
|
|
41
182
|
|
|
42
|
-
|
|
183
|
+
**✅ CORRECT - Map from constants**:
|
|
43
184
|
```tsx
|
|
44
|
-
//
|
|
45
|
-
|
|
185
|
+
// File: src/config/menuOptions.ts
|
|
186
|
+
export const productMenuItems = [
|
|
187
|
+
{ key: 'analytics', labelKey: 'product.analytics', href: NAVIGATION_URLS.PRODUCTS.ANALYTICS },
|
|
188
|
+
{ key: 'insights', labelKey: 'product.insights', href: NAVIGATION_URLS.PRODUCTS.INSIGHTS },
|
|
189
|
+
{ key: 'reports', labelKey: 'product.reports', href: NAVIGATION_URLS.PRODUCTS.REPORTS },
|
|
190
|
+
] as const;
|
|
191
|
+
|
|
192
|
+
// Usage:
|
|
193
|
+
import { productMenuItems } from '@/config/menuOptions';
|
|
194
|
+
import { useTranslations } from 'next-intl';
|
|
195
|
+
|
|
196
|
+
const t = useTranslations();
|
|
197
|
+
|
|
198
|
+
<DropdownMenu>
|
|
199
|
+
{productMenuItems.map((item) => (
|
|
200
|
+
<DropdownItem key={item.key} href={item.href}>
|
|
201
|
+
{t(item.labelKey)}
|
|
202
|
+
</DropdownItem>
|
|
203
|
+
))}
|
|
204
|
+
</DropdownMenu>
|
|
205
|
+
```
|
|
46
206
|
|
|
47
|
-
|
|
48
|
-
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## 🎨 Color Palette & Theme
|
|
210
|
+
|
|
211
|
+
### Primary Colors (from hero.ts)
|
|
212
|
+
|
|
213
|
+
The project uses a custom primary color palette defined in `src/styles/hero.ts`:
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
primary: {
|
|
217
|
+
DEFAULT: '#25C780', // Main primary color
|
|
218
|
+
50: '#E6FFF0', // Lightest
|
|
219
|
+
100: '#CBFFE0',
|
|
220
|
+
200: '#AEFFD1',
|
|
221
|
+
300: '#8EFFC2',
|
|
222
|
+
400: '#66FFB2',
|
|
223
|
+
500: '#19FFA3',
|
|
224
|
+
600: '#25C780', // DEFAULT
|
|
225
|
+
700: '#26915F',
|
|
226
|
+
800: '#215F40',
|
|
227
|
+
900: '#173123', // Darkest
|
|
228
|
+
}
|
|
229
|
+
```
|
|
49
230
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
231
|
+
### Using Primary Colors in Code
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
// Background colors
|
|
235
|
+
<div className="bg-primary"> {/* Uses DEFAULT (#25C780) */}
|
|
236
|
+
<div className="bg-primary-50"> {/* Lightest shade */}
|
|
237
|
+
<div className="bg-primary-600"> {/* Same as DEFAULT */}
|
|
238
|
+
<div className="bg-primary-900"> {/* Darkest shade */}
|
|
239
|
+
|
|
240
|
+
// Text colors
|
|
241
|
+
<p className="text-primary"> {/* Uses DEFAULT */}
|
|
242
|
+
<p className="text-primary-700">
|
|
243
|
+
|
|
244
|
+
// Border colors
|
|
245
|
+
<div className="border border-primary">
|
|
246
|
+
<div className="border-2 border-primary-500">
|
|
247
|
+
|
|
248
|
+
// HeroUI component colors
|
|
249
|
+
<Button color="primary">Click</Button> {/* Uses primary palette */}
|
|
54
250
|
```
|
|
55
251
|
|
|
252
|
+
### When to Use Which Shade
|
|
253
|
+
|
|
254
|
+
- **50-200**: Very light backgrounds, subtle highlights
|
|
255
|
+
- **300-400**: Hover states, light accents
|
|
256
|
+
- **500-600**: Primary actions, main brand color (DEFAULT is 600)
|
|
257
|
+
- **700-800**: Active states, darker accents
|
|
258
|
+
- **900**: Very dark accents, strong contrast
|
|
259
|
+
|
|
260
|
+
**Important**: Always use these theme colors instead of hardcoded hex values. This ensures consistency and makes theme updates easier.
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## 🧭 Navigation Components
|
|
265
|
+
|
|
266
|
+
### Navbar Component with HeroUI
|
|
267
|
+
|
|
268
|
+
When implementing navigation from Figma, use HeroUI Navbar component:
|
|
269
|
+
|
|
270
|
+
```tsx
|
|
271
|
+
'use client';
|
|
272
|
+
|
|
273
|
+
import { Navbar, NavbarBrand, NavbarContent, NavbarItem, NavbarMenuToggle, NavbarMenu, NavbarMenuItem } from '@heroui/react';
|
|
274
|
+
import Link from 'next/link';
|
|
275
|
+
import { useTranslations } from 'next-intl';
|
|
276
|
+
import { NAVIGATION_URLS } from '@/config/navigationUrls';
|
|
277
|
+
import { IMAGES } from '@/config/images';
|
|
278
|
+
|
|
279
|
+
// Define menu items in constants file
|
|
280
|
+
// File: src/config/navigationItems.ts
|
|
281
|
+
export const mainNavigationItems = [
|
|
282
|
+
{ key: 'home', labelKey: 'nav.home', href: NAVIGATION_URLS.ROOT },
|
|
283
|
+
{ key: 'about', labelKey: 'nav.about', href: NAVIGATION_URLS.ABOUT },
|
|
284
|
+
{ key: 'services', labelKey: 'nav.services', href: NAVIGATION_URLS.SERVICES },
|
|
285
|
+
{ key: 'contact', labelKey: 'nav.contact', href: NAVIGATION_URLS.CONTACT },
|
|
286
|
+
] as const;
|
|
287
|
+
|
|
288
|
+
export default function NavbarComponent() {
|
|
289
|
+
const t = useTranslations();
|
|
290
|
+
|
|
291
|
+
return (
|
|
292
|
+
<Navbar>
|
|
293
|
+
<NavbarBrand>
|
|
294
|
+
<Link href={NAVIGATION_URLS.ROOT}>
|
|
295
|
+
<img src={IMAGES.LOGOS.PRIMARY} alt={t('nav.logoAlt')} className="h-10 w-auto" />
|
|
296
|
+
</Link>
|
|
297
|
+
</NavbarBrand>
|
|
298
|
+
|
|
299
|
+
<NavbarContent className="hidden gap-4 sm:flex" justify="center">
|
|
300
|
+
{mainNavigationItems.map((item) => (
|
|
301
|
+
<NavbarItem key={item.key}>
|
|
302
|
+
<Link href={item.href} className="text-foreground">
|
|
303
|
+
{t(item.labelKey)}
|
|
304
|
+
</Link>
|
|
305
|
+
</NavbarItem>
|
|
306
|
+
))}
|
|
307
|
+
</NavbarContent>
|
|
308
|
+
|
|
309
|
+
<NavbarContent justify="end">
|
|
310
|
+
<NavbarItem>
|
|
311
|
+
<Button as={Link} href={NAVIGATION_URLS.AUTH.LOGIN} color="primary">
|
|
312
|
+
{t('nav.login')}
|
|
313
|
+
</Button>
|
|
314
|
+
</NavbarItem>
|
|
315
|
+
</NavbarContent>
|
|
316
|
+
|
|
317
|
+
<NavbarMenuToggle className="sm:hidden" />
|
|
318
|
+
|
|
319
|
+
<NavbarMenu>
|
|
320
|
+
{mainNavigationItems.map((item) => (
|
|
321
|
+
<NavbarMenuItem key={item.key}>
|
|
322
|
+
<Link href={item.href} className="w-full">
|
|
323
|
+
{t(item.labelKey)}
|
|
324
|
+
</Link>
|
|
325
|
+
</NavbarMenuItem>
|
|
326
|
+
))}
|
|
327
|
+
</NavbarMenu>
|
|
328
|
+
</Navbar>
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
**Key points**:
|
|
334
|
+
- Use HeroUI `Navbar`, `NavbarBrand`, `NavbarContent`, etc. components
|
|
335
|
+
- Define navigation items in separate constants file
|
|
336
|
+
- Use i18n for all text (`t('nav.home')`)
|
|
337
|
+
- Use `NAVIGATION_URLS` for all links (never hardcode)
|
|
338
|
+
- Use centralized image constants for logo (`IMAGES.LOGOS.PRIMARY`)
|
|
339
|
+
- Implement responsive menu with `NavbarMenuToggle` and `NavbarMenu`
|
|
340
|
+
|
|
56
341
|
---
|
|
57
342
|
|
|
58
343
|
## 📁 Project Structure
|
|
@@ -64,177 +349,380 @@ import { Input } from '@/components/ui'; // ✅ Use HeroUI Input wrapper
|
|
|
64
349
|
### Components Location
|
|
65
350
|
```
|
|
66
351
|
src/components/
|
|
67
|
-
├── ui/ # Reusable UI components (HeroUI wrappers)
|
|
68
|
-
├── icons/ #
|
|
352
|
+
├── ui/ # Reusable UI components (HeroUI wrappers with extendVariants)
|
|
353
|
+
├── icons/ # Auto-generated icon components (DO NOT manually edit)
|
|
69
354
|
├── layout/ # Layout components (Navbar, Footer)
|
|
70
355
|
└── [feature]/ # Feature-specific components
|
|
71
356
|
```
|
|
72
357
|
|
|
358
|
+
### Configuration Files
|
|
359
|
+
```
|
|
360
|
+
src/config/
|
|
361
|
+
├── navigationUrls.ts # All navigation URLs
|
|
362
|
+
├── navigationItems.ts # Navigation menu items
|
|
363
|
+
├── menuOptions.ts # Dropdown/select options
|
|
364
|
+
└── images.ts # Centralized image paths (CRITICAL)
|
|
365
|
+
```
|
|
366
|
+
|
|
73
367
|
### Styling
|
|
74
368
|
- **Global styles**: `src/styles/globals.css`
|
|
75
|
-
- **Theme config**: `src/styles/hero.ts`
|
|
369
|
+
- **Theme config**: `src/styles/hero.ts` (Primary colors defined here)
|
|
76
370
|
- **Approach**: Tailwind CSS utility classes ONLY
|
|
77
371
|
- **NO CSS modules, NO styled-components, NO inline styles**
|
|
78
372
|
|
|
79
373
|
---
|
|
80
374
|
|
|
81
|
-
##
|
|
375
|
+
## 🖼️ Icons
|
|
82
376
|
|
|
83
|
-
###
|
|
84
|
-
|
|
377
|
+
### CRITICAL WORKFLOW: Icon Component Pattern
|
|
378
|
+
|
|
379
|
+
**Modern Approach**: Use a centralized `Icon` component that dynamically renders icons:
|
|
85
380
|
|
|
86
|
-
#### Input Fields
|
|
87
381
|
```tsx
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
<Input
|
|
92
|
-
label="Email"
|
|
93
|
-
placeholder="Enter your email"
|
|
94
|
-
type="email"
|
|
95
|
-
isRequired
|
|
96
|
-
/>
|
|
382
|
+
// File: src/components/icons/Icon.tsx
|
|
383
|
+
import dynamic from 'next/dynamic';
|
|
384
|
+
import type { SVGProps } from 'react';
|
|
97
385
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
placeholder="Enter password"
|
|
102
|
-
isRequired
|
|
103
|
-
/>
|
|
386
|
+
interface IconProps extends SVGProps<SVGSVGElement> {
|
|
387
|
+
name: string; // Icon name without "Icon" suffix
|
|
388
|
+
}
|
|
104
389
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
max={100}
|
|
110
|
-
defaultValue={1}
|
|
111
|
-
/>
|
|
390
|
+
export function Icon({ name, ...props }: IconProps) {
|
|
391
|
+
const IconComponent = dynamic(() =>
|
|
392
|
+
import(`./index`).then((mod) => mod[`${name}Icon`])
|
|
393
|
+
);
|
|
112
394
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
/>
|
|
395
|
+
return <IconComponent {...props} />;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Usage in any component:
|
|
399
|
+
<Icon name="ChevronDown" className="size-5 text-white" />
|
|
400
|
+
<Icon name="Menu" className="size-6" />
|
|
401
|
+
<Icon name="Close" className="size-4 text-red-500" />
|
|
119
402
|
```
|
|
120
403
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
404
|
+
**Benefits**:
|
|
405
|
+
- No need to import individual icons
|
|
406
|
+
- Cleaner component code
|
|
407
|
+
- Easy to swap icons by changing name prop
|
|
408
|
+
- Consistent icon rendering
|
|
124
409
|
|
|
125
|
-
|
|
126
|
-
<Select
|
|
127
|
-
label="Country"
|
|
128
|
-
placeholder="Select country"
|
|
129
|
-
items={countries}
|
|
130
|
-
/>
|
|
410
|
+
### Icon Generation Workflow
|
|
131
411
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
{ value: 's', label: 'Small' },
|
|
137
|
-
{ value: 'm', label: 'Medium' },
|
|
138
|
-
{ value: 'l', label: 'Large' },
|
|
139
|
-
]}
|
|
140
|
-
/>
|
|
412
|
+
1. **Export SVG from Figma**
|
|
413
|
+
- Select icon in Figma
|
|
414
|
+
- Export as SVG (not PNG/JPG)
|
|
415
|
+
- Download the SVG file
|
|
141
416
|
|
|
142
|
-
|
|
143
|
-
|
|
417
|
+
2. **Place in Icons Folder**
|
|
418
|
+
```bash
|
|
419
|
+
mv downloaded-icon.svg src/assets/icons/icon-name.svg
|
|
420
|
+
```
|
|
144
421
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
422
|
+
3. **Generate React Components**
|
|
423
|
+
```bash
|
|
424
|
+
pnpm generate-icons
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
This command will:
|
|
428
|
+
- Convert all SVG files in `src/assets/icons/` to React components
|
|
429
|
+
- Place them in `src/components/icons/`
|
|
430
|
+
- Auto-format and lint the generated code
|
|
431
|
+
- Update `src/components/icons/index.ts` with exports
|
|
432
|
+
|
|
433
|
+
4. **Use with Icon Component**
|
|
434
|
+
```tsx
|
|
435
|
+
import { Icon } from '@/components/icons/Icon';
|
|
436
|
+
|
|
437
|
+
<Icon name="ChevronDown" className="size-5 text-white" />
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### Alternative: Direct Import (When Needed)
|
|
441
|
+
|
|
442
|
+
If you need direct imports for tree-shaking or specific use cases:
|
|
152
443
|
|
|
153
|
-
#### Date & Time
|
|
154
444
|
```tsx
|
|
155
|
-
import {
|
|
445
|
+
import { ChevronDownIcon, MenuIcon } from '@/components/icons';
|
|
156
446
|
|
|
157
|
-
<
|
|
158
|
-
|
|
159
|
-
placeholderValue={new CalendarDate(2024, 1, 1)}
|
|
160
|
-
/>
|
|
447
|
+
<ChevronDownIcon className="size-5 text-white" />
|
|
448
|
+
<MenuIcon className="size-6" />
|
|
161
449
|
```
|
|
162
450
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
451
|
+
**Available Icons** (auto-generated):
|
|
452
|
+
- ChevronDownIcon, ChevronRightIcon
|
|
453
|
+
- CloseIcon, MenuIcon
|
|
454
|
+
- EyeIcon, EyeSlashIcon
|
|
455
|
+
- AddIcon, AddCircleIcon
|
|
456
|
+
- ArrowUpIcon, ArrowCircleDownIcon
|
|
457
|
+
- DotsVerticalIcon, FrameIcon
|
|
458
|
+
- And more...
|
|
459
|
+
|
|
460
|
+
**❌ DO NOT**:
|
|
461
|
+
- Use Lucide, Font Awesome, or other icon libraries
|
|
462
|
+
- Create manual icon components
|
|
463
|
+
- Use `<img>` tags for icons
|
|
464
|
+
- Hardcode SVG paths directly in components
|
|
465
|
+
- Edit files in `src/components/icons/` manually (they are auto-generated)
|
|
466
|
+
|
|
467
|
+
**✅ DO**:
|
|
468
|
+
- Always export from Figma as SVG
|
|
469
|
+
- Use `pnpm generate-icons` to create components
|
|
470
|
+
- Use `<Icon name="IconName" />` pattern for cleaner code
|
|
471
|
+
- Use Tailwind classes for sizing (`size-4`, `size-5`, `size-6`)
|
|
472
|
+
- Organize icons in `src/assets/icons/` with descriptive names
|
|
166
473
|
|
|
167
|
-
|
|
168
|
-
Submit
|
|
169
|
-
</Button>
|
|
474
|
+
---
|
|
170
475
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
476
|
+
## 🖼️ Logos & Images
|
|
477
|
+
|
|
478
|
+
### CRITICAL WORKFLOW: Centralized Image Constants
|
|
479
|
+
|
|
480
|
+
**Step 1: Export from Figma**
|
|
481
|
+
|
|
482
|
+
1. **For logos**: Export as SVG (preferred) or PNG if source is raster
|
|
483
|
+
2. **For images**: Export as PNG, JPG, or WebP based on image type
|
|
484
|
+
3. Use appropriate resolution (2x or 3x for retina displays)
|
|
485
|
+
|
|
486
|
+
**Step 2: Organize in Public Folder**
|
|
487
|
+
|
|
488
|
+
```bash
|
|
489
|
+
public/
|
|
490
|
+
└── images/
|
|
491
|
+
├── logos/
|
|
492
|
+
│ ├── logo-circle-primary.svg
|
|
493
|
+
│ ├── logo-circle-secondary.svg
|
|
494
|
+
│ ├── logo-full.svg
|
|
495
|
+
│ ├── logo-full-black.svg
|
|
496
|
+
│ ├── logo-full-white.svg
|
|
497
|
+
│ └── logo-icon.svg
|
|
498
|
+
├── auth/
|
|
499
|
+
│ ├── login-background.jpg
|
|
500
|
+
│ ├── signup-background.jpg
|
|
501
|
+
│ └── reset-password.jpg
|
|
502
|
+
├── website/
|
|
503
|
+
│ ├── home-hero.webp
|
|
504
|
+
│ ├── home-newsletter.webp
|
|
505
|
+
│ ├── about-header.jpg
|
|
506
|
+
│ └── contact-map.png
|
|
507
|
+
├── services/
|
|
508
|
+
│ ├── services-header.webp
|
|
509
|
+
│ ├── feature-1.jpg
|
|
510
|
+
│ └── feature-2.jpg
|
|
511
|
+
└── products/
|
|
512
|
+
├── thumbnail-default.png
|
|
513
|
+
└── hero-banner.jpg
|
|
514
|
+
```
|
|
174
515
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
516
|
+
**Step 3: Create Centralized Image Constants**
|
|
517
|
+
|
|
518
|
+
**CRITICAL**: Always create and maintain `src/config/images.ts`:
|
|
519
|
+
|
|
520
|
+
```typescript
|
|
521
|
+
// src/config/images.ts
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Centralized image path constants
|
|
525
|
+
*
|
|
526
|
+
* CRITICAL RULES:
|
|
527
|
+
* 1. ALL image paths MUST be defined here
|
|
528
|
+
* 2. Organize by category (LOGOS, AUTH, WEBSITE, SERVICES, etc.)
|
|
529
|
+
* 3. Use descriptive UPPER_SNAKE_CASE names
|
|
530
|
+
* 4. Add `as const` for type safety
|
|
531
|
+
* 5. NEVER hardcode image paths in components
|
|
532
|
+
*
|
|
533
|
+
* Usage:
|
|
534
|
+
* import { IMAGES } from '@/config/images';
|
|
535
|
+
* <img src={IMAGES.LOGOS.PRIMARY_CIRCLE} alt="Logo" />
|
|
536
|
+
*/
|
|
537
|
+
|
|
538
|
+
const LOGOS = {
|
|
539
|
+
PRIMARY_CIRCLE: '/images/logos/logo-circle-primary.svg',
|
|
540
|
+
SECONDARY_CIRCLE: '/images/logos/logo-circle-secondary.svg',
|
|
541
|
+
FULL: '/images/logos/logo-full.svg',
|
|
542
|
+
FULL_BLACK: '/images/logos/logo-full-black.svg',
|
|
543
|
+
FULL_WHITE: '/images/logos/logo-full-white.svg',
|
|
544
|
+
ICON: '/images/logos/logo-icon.svg',
|
|
545
|
+
} as const;
|
|
546
|
+
|
|
547
|
+
const AUTH = {
|
|
548
|
+
LOGIN_BG: '/images/auth/login-background.jpg',
|
|
549
|
+
SIGNUP_BG: '/images/auth/signup-background.jpg',
|
|
550
|
+
RESET_PASSWORD: '/images/auth/reset-password.jpg',
|
|
551
|
+
} as const;
|
|
552
|
+
|
|
553
|
+
const WEBSITE = {
|
|
554
|
+
HOME_HERO: '/images/website/home-hero.webp',
|
|
555
|
+
HOME_NEWSLETTER: '/images/website/home-newsletter.webp',
|
|
556
|
+
ABOUT_HEADER: '/images/website/about-header.jpg',
|
|
557
|
+
CONTACT_MAP: '/images/website/contact-map.png',
|
|
558
|
+
} as const;
|
|
559
|
+
|
|
560
|
+
const SERVICES = {
|
|
561
|
+
HEADER: '/images/services/services-header.webp',
|
|
562
|
+
FEATURE_1: '/images/services/feature-1.jpg',
|
|
563
|
+
FEATURE_2: '/images/services/feature-2.jpg',
|
|
564
|
+
} as const;
|
|
565
|
+
|
|
566
|
+
const PRODUCTS = {
|
|
567
|
+
THUMBNAIL_DEFAULT: '/images/products/thumbnail-default.png',
|
|
568
|
+
HERO: '/images/products/hero-banner.jpg',
|
|
569
|
+
} as const;
|
|
570
|
+
|
|
571
|
+
export const IMAGES = {
|
|
572
|
+
LOGOS,
|
|
573
|
+
AUTH,
|
|
574
|
+
WEBSITE,
|
|
575
|
+
SERVICES,
|
|
576
|
+
PRODUCTS,
|
|
577
|
+
} as const;
|
|
178
578
|
```
|
|
179
579
|
|
|
180
|
-
|
|
580
|
+
**Step 4: Reference in Components**
|
|
581
|
+
|
|
181
582
|
```tsx
|
|
182
|
-
import {
|
|
583
|
+
import { IMAGES } from '@/config/images';
|
|
584
|
+
import Image from 'next/image';
|
|
585
|
+
|
|
586
|
+
// ✅ Using Next.js Image component (recommended for optimization)
|
|
587
|
+
<Image
|
|
588
|
+
src={IMAGES.LOGOS.FULL}
|
|
589
|
+
alt="Company Logo"
|
|
590
|
+
width={120}
|
|
591
|
+
height={40}
|
|
592
|
+
priority
|
|
593
|
+
/>
|
|
183
594
|
|
|
184
|
-
//
|
|
185
|
-
<
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
595
|
+
// ✅ Using standard img tag (for SVGs with CSS control)
|
|
596
|
+
<img src={IMAGES.LOGOS.PRIMARY_CIRCLE} alt="Logo" className="h-10 w-auto" />
|
|
597
|
+
|
|
598
|
+
// ✅ Background images via Tailwind
|
|
599
|
+
<div
|
|
600
|
+
className="bg-cover bg-center"
|
|
601
|
+
style={{ backgroundImage: `url(${IMAGES.WEBSITE.HOME_HERO})` }}
|
|
189
602
|
>
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
// Breadcrumbs
|
|
199
|
-
<Breadcrumbs
|
|
200
|
-
items={[
|
|
201
|
-
{ label: 'Home', href: '/' },
|
|
202
|
-
{ label: 'Products', href: '/products' },
|
|
203
|
-
]}
|
|
603
|
+
|
|
604
|
+
// ✅ In authentication pages
|
|
605
|
+
<Image
|
|
606
|
+
src={IMAGES.AUTH.LOGIN_BG}
|
|
607
|
+
alt="Login background"
|
|
608
|
+
fill
|
|
609
|
+
className="object-cover"
|
|
204
610
|
/>
|
|
205
611
|
|
|
206
|
-
//
|
|
207
|
-
<
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
612
|
+
// ✅ In service pages
|
|
613
|
+
<Image
|
|
614
|
+
src={IMAGES.SERVICES.HEADER}
|
|
615
|
+
alt="Services"
|
|
616
|
+
width={1920}
|
|
617
|
+
height={600}
|
|
618
|
+
className="w-full"
|
|
212
619
|
/>
|
|
213
620
|
```
|
|
214
621
|
|
|
215
|
-
###
|
|
622
|
+
### Image Format Preference Order
|
|
216
623
|
|
|
217
|
-
**
|
|
218
|
-
|
|
219
|
-
|
|
624
|
+
1. ✅ **SVG** - Best for logos, icons, and vector graphics (scalable, small file size)
|
|
625
|
+
2. ✅ **WebP** - Modern format for photos (better compression than PNG/JPG)
|
|
626
|
+
3. ✅ **PNG** - For images requiring transparency
|
|
627
|
+
4. ✅ **JPG** - For photos without transparency
|
|
220
628
|
|
|
221
|
-
|
|
222
|
-
import { ChevronDownIcon, ChevronRightIcon, CloseIcon, EyeIcon, EyeSlashIcon } from '@/components/icons';
|
|
629
|
+
### CRITICAL: Check Before Export Workflow
|
|
223
630
|
|
|
224
|
-
|
|
225
|
-
```
|
|
631
|
+
Before exporting any logo or image from Figma:
|
|
226
632
|
|
|
227
|
-
**
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
633
|
+
1. **Check if it already exists** in `public/images/`
|
|
634
|
+
2. **Check if it's already defined** in `src/config/images.ts`
|
|
635
|
+
3. **Use existing constant** if available
|
|
636
|
+
4. **Only export and add new** if it doesn't exist
|
|
231
637
|
|
|
232
|
-
|
|
638
|
+
This prevents:
|
|
639
|
+
- Duplicate images in the project
|
|
640
|
+
- Inconsistent image usage
|
|
641
|
+
- Bloated public folder
|
|
642
|
+
- Broken image references
|
|
643
|
+
|
|
644
|
+
**Example**:
|
|
645
|
+
```tsx
|
|
646
|
+
// ❌ WRONG - Exporting logo that already exists
|
|
647
|
+
// User asks: "Add company logo to header"
|
|
648
|
+
// You: Export logo from Figma, add to public/, create new constant
|
|
649
|
+
|
|
650
|
+
// ✅ CORRECT - Check first, then use existing
|
|
651
|
+
// 1. Check src/config/images.ts
|
|
652
|
+
// 2. See IMAGES.LOGOS.FULL already exists
|
|
653
|
+
// 3. Use it: <img src={IMAGES.LOGOS.FULL} alt="Logo" />
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
**❌ DO NOT**:
|
|
657
|
+
- Put images in `src/` folder
|
|
658
|
+
- Import images as modules unless necessary
|
|
659
|
+
- Use base64 encoded images inline
|
|
660
|
+
- Forget to optimize images before adding
|
|
661
|
+
- **Hardcode image paths directly in components** (CRITICAL ERROR)
|
|
662
|
+
- Export images that already exist in the project
|
|
663
|
+
|
|
664
|
+
**✅ DO**:
|
|
665
|
+
- Always place images in `public/images/` with organized folders
|
|
666
|
+
- **Create and maintain `src/config/images.ts`** (CRITICAL)
|
|
667
|
+
- Organize image paths by category (LOGOS, AUTH, WEBSITE, SERVICES, PRODUCTS)
|
|
668
|
+
- Use `as const` for type safety
|
|
669
|
+
- Import from `@/config/images` in ALL components
|
|
670
|
+
- Check if image exists before exporting from Figma
|
|
671
|
+
- Prefer SVG for logos and icons
|
|
672
|
+
- Optimize images before adding (use tools like ImageOptim, Squoosh)
|
|
673
|
+
- Use Next.js `<Image>` component for automatic optimization
|
|
233
674
|
|
|
234
675
|
---
|
|
235
676
|
|
|
236
677
|
## 🎨 Tailwind CSS Guidelines
|
|
237
678
|
|
|
679
|
+
### Using Standard Tailwind Classes
|
|
680
|
+
|
|
681
|
+
**CRITICAL**: Always use closest standard Tailwind class instead of custom arbitrary values
|
|
682
|
+
|
|
683
|
+
**Common Conversions**:
|
|
684
|
+
```tsx
|
|
685
|
+
// ❌ WRONG - Custom arbitrary values
|
|
686
|
+
className="h-[41px] w-[54.6px]"
|
|
687
|
+
className="p-[24px]"
|
|
688
|
+
className="text-[14px]"
|
|
689
|
+
className="gap-[11px]"
|
|
690
|
+
|
|
691
|
+
// ✅ CORRECT - Standard Tailwind classes
|
|
692
|
+
className="h-10 w-14" // h-10 = 40px, w-14 = 56px (closest to 54.6px)
|
|
693
|
+
className="p-6" // p-6 = 24px
|
|
694
|
+
className="text-sm" // text-sm = 14px
|
|
695
|
+
className="gap-3" // gap-3 = 12px (closest to 11px)
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
**Tailwind Size Reference**:
|
|
699
|
+
```css
|
|
700
|
+
/* Spacing Scale (padding, margin, gap, etc.) */
|
|
701
|
+
0.5 = 2px 4 = 16px 12 = 48px
|
|
702
|
+
1 = 4px 5 = 20px 14 = 56px
|
|
703
|
+
2 = 8px 6 = 24px 16 = 64px
|
|
704
|
+
3 = 12px 8 = 32px 20 = 80px
|
|
705
|
+
3.5 = 14px 10 = 40px 24 = 96px
|
|
706
|
+
|
|
707
|
+
/* Font Sizes */
|
|
708
|
+
xs = 12px (0.75rem)
|
|
709
|
+
sm = 14px (0.875rem)
|
|
710
|
+
base = 16px (1rem)
|
|
711
|
+
lg = 18px (1.125rem)
|
|
712
|
+
xl = 20px (1.25rem)
|
|
713
|
+
2xl = 24px (1.5rem)
|
|
714
|
+
3xl = 30px (1.875rem)
|
|
715
|
+
|
|
716
|
+
/* Width/Height */
|
|
717
|
+
auto, full, screen, fit, min, max
|
|
718
|
+
4-96 (in increments of 4px)
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
**Only use arbitrary values when**:
|
|
722
|
+
- Exact pixel value is required by design system
|
|
723
|
+
- No standard Tailwind class is close enough
|
|
724
|
+
- Using design tokens/CSS variables: `bg-[var(--custom-color)]`
|
|
725
|
+
|
|
238
726
|
### Responsive Breakpoints (Mobile-First)
|
|
239
727
|
```css
|
|
240
728
|
/* Mobile */
|
|
@@ -255,124 +743,53 @@ xl: 1280px /* MacBook Air */
|
|
|
255
743
|
4xl: 1728px /* MacBook Pro 16" */
|
|
256
744
|
```
|
|
257
745
|
|
|
258
|
-
### Usage Example
|
|
259
|
-
```tsx
|
|
260
|
-
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
261
|
-
{/* Mobile: 1 column, Tablet: 2 columns, Desktop: 3 columns */}
|
|
262
|
-
</div>
|
|
263
|
-
```
|
|
264
|
-
|
|
265
746
|
### HeroUI Color System
|
|
747
|
+
|
|
266
748
|
Use these semantic colors from HeroUI theme:
|
|
267
749
|
- `bg-background` - Main background
|
|
268
750
|
- `bg-foreground` - Text color background
|
|
269
751
|
- `bg-default` / `bg-default-50` to `bg-default-900` - Gray scale
|
|
270
|
-
- `bg-primary` / `text-primary` - Primary brand color
|
|
752
|
+
- `bg-primary` / `text-primary` - Primary brand color (#25C780)
|
|
753
|
+
- `bg-primary-50` to `bg-primary-900` - Primary color shades
|
|
271
754
|
- `bg-secondary` - Secondary color
|
|
272
755
|
- `bg-success` - Success states
|
|
273
756
|
- `bg-warning` - Warning states
|
|
274
757
|
- `bg-danger` - Error/danger states
|
|
275
758
|
|
|
276
|
-
### Common Patterns
|
|
277
|
-
```tsx
|
|
278
|
-
// Card
|
|
279
|
-
<div className="rounded-lg bg-white p-6 shadow-md">
|
|
280
|
-
|
|
281
|
-
// Container
|
|
282
|
-
<div className="container mx-auto px-4">
|
|
283
|
-
|
|
284
|
-
// Flexbox centering
|
|
285
|
-
<div className="flex items-center justify-center">
|
|
286
|
-
|
|
287
|
-
// Grid layout
|
|
288
|
-
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
289
|
-
|
|
290
|
-
// Spacing
|
|
291
|
-
<div className="space-y-4"> {/* Vertical spacing */}
|
|
292
|
-
<div className="space-x-4"> {/* Horizontal spacing */}
|
|
293
|
-
```
|
|
294
|
-
|
|
295
759
|
---
|
|
296
760
|
|
|
297
|
-
##
|
|
761
|
+
## 🌐 Internationalization (i18n)
|
|
298
762
|
|
|
299
|
-
|
|
300
|
-
```tsx
|
|
301
|
-
'use client';
|
|
763
|
+
**CRITICAL**: ALL text content MUST use i18n translation keys
|
|
302
764
|
|
|
303
|
-
|
|
304
|
-
import { useForm } from 'react-hook-form';
|
|
305
|
-
import { z } from 'zod';
|
|
765
|
+
### Navigation with i18n
|
|
306
766
|
|
|
307
|
-
|
|
767
|
+
**Import Guidelines**:
|
|
768
|
+
```tsx
|
|
769
|
+
// For Link component - use Next.js Link
|
|
770
|
+
import Link from 'next/link';
|
|
308
771
|
|
|
309
|
-
//
|
|
310
|
-
|
|
311
|
-
email: z.string().email('Invalid email'),
|
|
312
|
-
password: z.string().min(8, 'Password must be at least 8 characters'),
|
|
313
|
-
});
|
|
772
|
+
// For navigation hooks - use next/navigation
|
|
773
|
+
import { usePathname, useRouter } from 'next/navigation';
|
|
314
774
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
export default function LoginForm() {
|
|
318
|
-
// 2. Setup form
|
|
319
|
-
const {
|
|
320
|
-
register,
|
|
321
|
-
handleSubmit,
|
|
322
|
-
formState: { errors, isSubmitting },
|
|
323
|
-
} = useForm<FormData>({
|
|
324
|
-
resolver: zodResolver(schema),
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
// 3. Submit handler
|
|
328
|
-
const onSubmit = async (data: FormData) => {
|
|
329
|
-
// Handle form submission
|
|
330
|
-
console.log(data);
|
|
331
|
-
};
|
|
332
|
-
|
|
333
|
-
// 4. Render form
|
|
334
|
-
return (
|
|
335
|
-
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
|
|
336
|
-
<Input
|
|
337
|
-
label="Email"
|
|
338
|
-
type="email"
|
|
339
|
-
{...register('email')}
|
|
340
|
-
errorMessage={errors.email?.message}
|
|
341
|
-
isInvalid={!!errors.email}
|
|
342
|
-
/>
|
|
343
|
-
|
|
344
|
-
<Input
|
|
345
|
-
label="Password"
|
|
346
|
-
type="password"
|
|
347
|
-
{...register('password')}
|
|
348
|
-
errorMessage={errors.password?.message}
|
|
349
|
-
isInvalid={!!errors.password}
|
|
350
|
-
/>
|
|
351
|
-
|
|
352
|
-
<Button type="submit" isLoading={isSubmitting} className="w-full">
|
|
353
|
-
Submit
|
|
354
|
-
</Button>
|
|
355
|
-
</form>
|
|
356
|
-
);
|
|
357
|
-
}
|
|
775
|
+
// For navigation URLs - use centralized constants
|
|
776
|
+
import { NAVIGATION_URLS } from '@/config/navigationUrls';
|
|
358
777
|
```
|
|
359
778
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
## 🌐 Internationalization (i18n)
|
|
363
|
-
|
|
364
|
-
**CRITICAL**: ALL text content MUST use i18n translation keys
|
|
365
|
-
|
|
366
|
-
### Navigation with i18n
|
|
779
|
+
**Navigation Example**:
|
|
367
780
|
```tsx
|
|
368
|
-
import
|
|
781
|
+
import Link from 'next/link';
|
|
369
782
|
import { NAVIGATION_URLS } from '@/config/navigationUrls';
|
|
370
783
|
|
|
371
|
-
// ✅ CORRECT - Using NAVIGATION_URLS
|
|
784
|
+
// ✅ CORRECT - Using Next.js Link with NAVIGATION_URLS
|
|
372
785
|
<Link href={NAVIGATION_URLS.ABOUT}>About</Link>
|
|
373
786
|
|
|
374
787
|
// ❌ WRONG - Hardcoded URL
|
|
375
788
|
<Link href="/about">About</Link>
|
|
789
|
+
|
|
790
|
+
// ❌ WRONG - Using HeroUI Link for internal navigation
|
|
791
|
+
import { Link } from '@heroui/react';
|
|
792
|
+
<Link href={NAVIGATION_URLS.ABOUT}>About</Link>
|
|
376
793
|
```
|
|
377
794
|
|
|
378
795
|
### Translations for ALL text
|
|
@@ -395,152 +812,77 @@ function MyComponent() {
|
|
|
395
812
|
}
|
|
396
813
|
```
|
|
397
814
|
|
|
398
|
-
### Form labels, placeholders, errors - ALL must use i18n
|
|
399
|
-
```tsx
|
|
400
|
-
import { useTranslations } from 'next-intl';
|
|
401
|
-
|
|
402
|
-
function LoginForm() {
|
|
403
|
-
const t = useTranslations();
|
|
404
|
-
|
|
405
|
-
return (
|
|
406
|
-
<form>
|
|
407
|
-
{/* ✅ CORRECT */}
|
|
408
|
-
<Input
|
|
409
|
-
label={t('email')}
|
|
410
|
-
placeholder={t('enterEmail')}
|
|
411
|
-
errorMessage={errors.email?.message ? t(errors.email.message) : undefined}
|
|
412
|
-
/>
|
|
413
|
-
|
|
414
|
-
{/* ❌ WRONG - Hardcoded text */}
|
|
415
|
-
<Input
|
|
416
|
-
label="Email"
|
|
417
|
-
placeholder="Enter your email"
|
|
418
|
-
errorMessage="Invalid email"
|
|
419
|
-
/>
|
|
420
|
-
</form>
|
|
421
|
-
);
|
|
422
|
-
}
|
|
423
|
-
```
|
|
424
|
-
|
|
425
|
-
### Navigation URLs Reference
|
|
426
|
-
```tsx
|
|
427
|
-
import { NAVIGATION_URLS } from '@/config/navigationUrls';
|
|
428
|
-
|
|
429
|
-
// Available URLs (based on project type):
|
|
430
|
-
NAVIGATION_URLS.ROOT // '/'
|
|
431
|
-
NAVIGATION_URLS.ABOUT // '/about'
|
|
432
|
-
NAVIGATION_URLS.CONTACT // '/contact'
|
|
433
|
-
NAVIGATION_URLS.PRODUCTS.INDEX // '/products'
|
|
434
|
-
NAVIGATION_URLS.PRODUCTS.DETAIL('123') // '/products/123'
|
|
435
|
-
NAVIGATION_URLS.AUTH.LOGIN // '/auth/login'
|
|
436
|
-
NAVIGATION_URLS.DASHBOARD.INDEX // '/dashboard'
|
|
437
|
-
|
|
438
|
-
// ✅ ALWAYS use NAVIGATION_URLS
|
|
439
|
-
<Link href={NAVIGATION_URLS.PRODUCTS.INDEX}>Products</Link>
|
|
440
|
-
|
|
441
|
-
// ❌ NEVER hardcode URLs
|
|
442
|
-
<Link href="/products">Products</Link>
|
|
443
|
-
```
|
|
444
|
-
|
|
445
|
-
---
|
|
446
|
-
|
|
447
|
-
## 🎯 Data Fetching
|
|
448
|
-
|
|
449
|
-
### Using React Query
|
|
450
|
-
```tsx
|
|
451
|
-
'use client';
|
|
452
|
-
|
|
453
|
-
import { useQuery } from '@tanstack/react-query';
|
|
454
|
-
import axios from 'axios';
|
|
455
|
-
|
|
456
|
-
import { QUERY_KEYS } from '@/libs/react-query';
|
|
457
|
-
|
|
458
|
-
export default function ProductList() {
|
|
459
|
-
const { data, isLoading, error } = useQuery({
|
|
460
|
-
queryKey: QUERY_KEYS.PRODUCTS.LIST,
|
|
461
|
-
queryFn: async () => {
|
|
462
|
-
const response = await axios.get('/api/products');
|
|
463
|
-
return response.data;
|
|
464
|
-
},
|
|
465
|
-
});
|
|
466
|
-
|
|
467
|
-
if (isLoading) return <div>Loading...</div>;
|
|
468
|
-
if (error) return <div>Error loading products</div>;
|
|
469
|
-
|
|
470
|
-
return (
|
|
471
|
-
<div>
|
|
472
|
-
{data.map((product) => (
|
|
473
|
-
<div key={product.id}>{product.name}</div>
|
|
474
|
-
))}
|
|
475
|
-
</div>
|
|
476
|
-
);
|
|
477
|
-
}
|
|
478
|
-
```
|
|
479
|
-
|
|
480
|
-
---
|
|
481
|
-
|
|
482
|
-
## 📐 Layout Components
|
|
483
|
-
|
|
484
|
-
### Using Existing Layouts
|
|
485
|
-
```tsx
|
|
486
|
-
// Auth pages use auth layout automatically
|
|
487
|
-
// Location: src/app/[locale]/(auth)/login/page.tsx
|
|
488
|
-
|
|
489
|
-
'use client';
|
|
490
|
-
|
|
491
|
-
export default function LoginPage() {
|
|
492
|
-
return (
|
|
493
|
-
<div className="w-full max-w-md space-y-6 rounded-lg bg-white p-8 shadow-md">
|
|
494
|
-
<h1 className="text-center text-2xl font-bold">Login</h1>
|
|
495
|
-
{/* Your form here */}
|
|
496
|
-
</div>
|
|
497
|
-
);
|
|
498
|
-
}
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
### Navbar & Footer
|
|
502
|
-
Already implemented in `src/components/layout/`:
|
|
503
|
-
- `Navbar.tsx` - Main navigation
|
|
504
|
-
- `Footer.tsx` - Footer component
|
|
505
|
-
|
|
506
|
-
Use in root layout:
|
|
507
|
-
```tsx
|
|
508
|
-
import { Footer, Navbar } from '@/components/layout';
|
|
509
|
-
|
|
510
|
-
<Navbar />
|
|
511
|
-
<main>{children}</main>
|
|
512
|
-
<Footer />
|
|
513
|
-
```
|
|
514
|
-
|
|
515
815
|
---
|
|
516
816
|
|
|
517
817
|
## ⚠️ CRITICAL RULES
|
|
518
818
|
|
|
519
819
|
### ❌ DO NOT
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
820
|
+
|
|
821
|
+
1. ❌ **Figma MCP**: Manually implement design without fetching from Figma MCP server
|
|
822
|
+
2. ❌ **Figma MCP**: Ask user to describe design - always use Figma MCP to fetch
|
|
823
|
+
3. ❌ **Icons**: Create manual icon components - MUST use `pnpm generate-icons` workflow
|
|
824
|
+
4. ❌ **Icons**: Use non-project icons (Lucide, Font Awesome, etc.)
|
|
825
|
+
5. ❌ **Icons**: Edit auto-generated files in `src/components/icons/` manually
|
|
826
|
+
6. ❌ **Logos**: Put logos/images in `src/` folder - MUST go in `public/images/`
|
|
827
|
+
7. ❌ **Logos**: Import images as modules unless necessary
|
|
828
|
+
8. ❌ **Logos**: Hardcode image paths in components - MUST use `@/config/images` constants
|
|
829
|
+
9. ❌ **Logos**: Export images that already exist - check `src/config/images.ts` first
|
|
830
|
+
10. ❌ **Logos**: Skip creating centralized image constants file
|
|
831
|
+
11. ❌ **Colors**: Hardcode hex values when theme colors exist (use `bg-primary`, not `bg-[#25C780]`)
|
|
832
|
+
12. ❌ **Tailwind**: Use arbitrary values when standard classes exist (e.g., `h-[40px]` → use `h-10`)
|
|
833
|
+
13. ❌ **Tailwind**: Use custom values like `w-[100%]` (use `w-full`)
|
|
834
|
+
14. ❌ **Text**: Hardcode ANY text - ALL text MUST use i18n translation keys
|
|
835
|
+
15. ❌ **URLs**: Hardcode URLs - ALWAYS use `NAVIGATION_URLS` from config
|
|
836
|
+
16. ❌ **Data**: Hardcode menu items, dropdown options - MUST map from constants
|
|
837
|
+
17. ❌ **Components**: Create custom components when HeroUI provides them
|
|
838
|
+
18. ❌ **Components**: Use HTML elements when HeroUI component exists (e.g., `<button>` → use `<Button>`)
|
|
839
|
+
19. ❌ **Styling**: Apply component styles inline - MUST configure with `extendVariants` and `defaultVariants`
|
|
840
|
+
20. ❌ **Styling**: Use CSS modules or styled-components - ONLY Tailwind CSS
|
|
841
|
+
21. ❌ **Imports**: Import from `@heroui/react` directly (use `@/components/ui` wrappers)
|
|
842
|
+
22. ❌ **Navigation**: Use HeroUI `Link` for internal navigation (use Next.js `Link` from `next/link`)
|
|
843
|
+
23. ❌ **Navigation**: Import `usePathname`, `useRouter` from wrong source (MUST use `next/navigation`)
|
|
844
|
+
24. ❌ **TypeScript**: Use `any` type
|
|
845
|
+
25. ❌ **Design**: Ignore responsive design (always implement mobile-first)
|
|
846
|
+
26. ❌ **Forms**: Skip form validation (always use Zod schemas)
|
|
530
847
|
|
|
531
848
|
### ✅ DO
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
849
|
+
|
|
850
|
+
1. ✅ **Figma MCP**: Always use Figma MCP server to fetch design details
|
|
851
|
+
2. ✅ **Figma MCP**: Analyze Figma structure before implementation (pages, frames, components)
|
|
852
|
+
3. ✅ **Figma MCP**: Extract design tokens (colors, spacing, typography) from Figma
|
|
853
|
+
4. ✅ **Figma MCP**: Map Figma components to HeroUI components before coding
|
|
854
|
+
5. ✅ **Icons**: Export SVG from Figma → `src/assets/icons/` → Run `pnpm generate-icons`
|
|
855
|
+
6. ✅ **Icons**: Use `<Icon name="IconName" />` pattern for cleaner code
|
|
856
|
+
7. ✅ **Icons**: Import from `@/components/icons` (auto-generated components)
|
|
857
|
+
8. ✅ **Logos**: Export SVG/PNG from Figma → Place in `public/images/` with organized folders
|
|
858
|
+
9. ✅ **Logos**: Create/update centralized constants file `src/config/images.ts`
|
|
859
|
+
10. ✅ **Logos**: Organize image paths by category (LOGOS, AUTH, WEBSITE, SERVICES, PRODUCTS)
|
|
860
|
+
11. ✅ **Logos**: Import from `@/config/images` and use `IMAGES.CATEGORY.NAME`
|
|
861
|
+
12. ✅ **Logos**: Check if image exists in `src/config/images.ts` before exporting from Figma
|
|
862
|
+
13. ✅ **Logos**: Prefer SVG over PNG when source is vector
|
|
863
|
+
14. ✅ **Colors**: Use theme colors (`bg-primary`, `bg-primary-600`, `text-primary`)
|
|
864
|
+
15. ✅ **Colors**: Reference primary color shades (50-900) for consistent branding
|
|
865
|
+
16. ✅ **Tailwind**: Use standard classes (`h-10`, `p-6`, `text-sm` instead of arbitrary values)
|
|
866
|
+
17. ✅ **Tailwind**: Use size utilities (`size-4`, `size-5`) for icons
|
|
867
|
+
18. ✅ **Navigation**: Use Next.js `Link` from `next/link` for internal navigation
|
|
868
|
+
19. ✅ **Navigation**: Import `usePathname`, `useRouter` from `next/navigation`
|
|
869
|
+
20. ✅ **Navigation**: Use HeroUI Navbar component for main navigation
|
|
870
|
+
21. ✅ **Text**: Use i18n translation keys for ALL text (`t('key')`)
|
|
871
|
+
22. ✅ **URLs**: Use `NAVIGATION_URLS` for ALL navigation links
|
|
872
|
+
23. ✅ **Data**: Map menu items, dropdown options from constants (never hardcode)
|
|
873
|
+
24. ✅ **Data**: Define navigation items in `src/config/navigationItems.ts`
|
|
874
|
+
25. ✅ **Components**: Use HeroUI components first (check library before custom)
|
|
875
|
+
26. ✅ **Components**: Use existing wrappers from `src/components/ui/` (e.g., `<Button>` not `<button>`)
|
|
876
|
+
27. ✅ **Components**: Configure styles with `extendVariants` and `defaultVariants`, not inline
|
|
877
|
+
28. ✅ **Components**: Keep component usage clean (let defaultVariants handle defaults)
|
|
878
|
+
29. ✅ **Styling**: Use Tailwind CSS ONLY (utility classes)
|
|
879
|
+
30. ✅ **TypeScript**: Implement strict typing with proper types
|
|
880
|
+
31. ✅ **React**: Use `'use client'` directive for interactive components
|
|
881
|
+
32. ✅ **Design**: Follow mobile-first responsive design
|
|
882
|
+
33. ✅ **Design**: Implement responsive menu with HeroUI NavbarMenuToggle
|
|
883
|
+
34. ✅ **States**: Implement proper loading and error states
|
|
884
|
+
35. ✅ **Patterns**: Follow existing code patterns and naming conventions
|
|
885
|
+
36. ✅ **Patterns**: Use `as const` for type safety in constants
|
|
544
886
|
|
|
545
887
|
---
|
|
546
888
|
|
|
@@ -548,26 +890,65 @@ import { Footer, Navbar } from '@/components/layout';
|
|
|
548
890
|
|
|
549
891
|
When converting Figma to code, ensure:
|
|
550
892
|
|
|
893
|
+
### Figma Integration
|
|
894
|
+
- [ ] Used Figma MCP server to fetch design
|
|
895
|
+
- [ ] Analyzed all pages and components in Figma file
|
|
896
|
+
- [ ] Identified design tokens (colors, spacing, typography)
|
|
897
|
+
- [ ] Mapped Figma components to HeroUI components
|
|
898
|
+
- [ ] Extracted all necessary assets (icons, images)
|
|
899
|
+
|
|
900
|
+
### Icons & Assets
|
|
901
|
+
- [ ] Exported icons as SVG from Figma
|
|
902
|
+
- [ ] Placed SVG files in `src/assets/icons/`
|
|
903
|
+
- [ ] Ran `pnpm generate-icons` to create React components
|
|
904
|
+
- [ ] Used `<Icon name="IconName" />` pattern in code
|
|
905
|
+
- [ ] Checked if logos/images already exist before export
|
|
906
|
+
- [ ] Exported new logos/images as SVG (preferred) or PNG
|
|
907
|
+
- [ ] Placed logos/images in `public/images/` with organized folders (logos/, auth/, website/, services/)
|
|
908
|
+
- [ ] Created/updated centralized constants file `src/config/images.ts`
|
|
909
|
+
- [ ] Organized image paths by category (LOGOS, AUTH, WEBSITE, SERVICES, PRODUCTS)
|
|
910
|
+
- [ ] Used `as const` for type safety
|
|
911
|
+
- [ ] Referenced via `IMAGES.CATEGORY.NAME` (not hardcoded paths)
|
|
912
|
+
|
|
913
|
+
### Colors & Theme
|
|
914
|
+
- [ ] Used primary colors from theme (`bg-primary`, `bg-primary-600`)
|
|
915
|
+
- [ ] Applied correct color shades for states (hover, active, disabled)
|
|
916
|
+
- [ ] Followed HeroUI semantic colors (background, foreground, success, danger)
|
|
917
|
+
- [ ] No hardcoded hex values when theme colors available
|
|
918
|
+
|
|
551
919
|
### Component & Styling
|
|
552
920
|
- [ ] Checked HeroUI library first for component availability
|
|
553
921
|
- [ ] Used HeroUI components via `src/components/ui/` wrappers
|
|
554
|
-
- [ ] Used
|
|
922
|
+
- [ ] Used `extendVariants` pattern with `defaultVariants` for component configuration
|
|
923
|
+
- [ ] Kept component usage clean (no repetitive className props)
|
|
924
|
+
- [ ] Used only layout utilities inline (`mt-4`, `hidden`, `lg:block`)
|
|
555
925
|
- [ ] Applied Tailwind CSS classes only (no custom CSS, no CSS modules)
|
|
926
|
+
- [ ] Used standard Tailwind classes instead of arbitrary values
|
|
556
927
|
- [ ] Followed HeroUI theme colors (bg-background, bg-primary, etc.)
|
|
928
|
+
- [ ] Mapped all data from constants (no hardcoded menu items/dropdowns)
|
|
929
|
+
- [ ] Created constants files for navigation items and menu options
|
|
557
930
|
|
|
558
|
-
###
|
|
931
|
+
### Navigation
|
|
932
|
+
- [ ] Used Next.js `Link` from `next/link` for internal navigation
|
|
933
|
+
- [ ] Used `NAVIGATION_URLS` for ALL links (no hardcoded URLs)
|
|
934
|
+
- [ ] Imported `usePathname`, `useRouter` from `next/navigation`
|
|
935
|
+
- [ ] Implemented responsive navigation with HeroUI Navbar components
|
|
936
|
+
- [ ] Created navigation items constants in `src/config/navigationItems.ts`
|
|
937
|
+
|
|
938
|
+
### Content & i18n
|
|
559
939
|
- [ ] ALL text uses i18n translation keys (no hardcoded text)
|
|
560
940
|
- [ ] ALL labels use `t('labelKey')`
|
|
561
941
|
- [ ] ALL placeholders use `t('placeholderKey')`
|
|
562
942
|
- [ ] ALL error messages use `t('errorKey')`
|
|
563
943
|
- [ ] ALL titles and headings use `t('titleKey')`
|
|
564
|
-
- [ ] ALL
|
|
944
|
+
- [ ] ALL button text use `t('buttonKey')`
|
|
565
945
|
|
|
566
946
|
### Structure & Types
|
|
567
947
|
- [ ] Page created in correct location (`src/app/[locale]/...`)
|
|
568
948
|
- [ ] Added TypeScript types for all data structures
|
|
569
949
|
- [ ] Used `'use client'` for interactive components
|
|
570
950
|
- [ ] No `any` types used
|
|
951
|
+
- [ ] Used `as const` for constants type safety
|
|
571
952
|
|
|
572
953
|
### Forms & Validation
|
|
573
954
|
- [ ] Implemented forms with Zod validation schemas
|
|
@@ -580,6 +961,7 @@ When converting Figma to code, ensure:
|
|
|
580
961
|
- [ ] Added proper loading states
|
|
581
962
|
- [ ] Added proper error states
|
|
582
963
|
- [ ] Tested on multiple breakpoints (mobile, tablet, desktop)
|
|
964
|
+
- [ ] Implemented responsive navigation menu
|
|
583
965
|
|
|
584
966
|
---
|
|
585
967
|
|
|
@@ -587,38 +969,217 @@ When converting Figma to code, ensure:
|
|
|
587
969
|
|
|
588
970
|
Before finalizing code, verify:
|
|
589
971
|
|
|
590
|
-
###
|
|
591
|
-
1. **
|
|
592
|
-
2. **
|
|
593
|
-
3. **
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
972
|
+
### Figma Integration
|
|
973
|
+
1. **Figma MCP used** - Design fetched via Figma MCP server (not manual)
|
|
974
|
+
2. **Design tokens extracted** - Colors, spacing, typography identified
|
|
975
|
+
3. **Component mapping** - Figma components mapped to HeroUI
|
|
976
|
+
|
|
977
|
+
### Icons & Assets
|
|
978
|
+
1. **Icons workflow** - All icons created via `pnpm generate-icons` (not manual)
|
|
979
|
+
2. **Icon usage** - Using `<Icon name="IconName" />` pattern
|
|
980
|
+
3. **Icons source** - SVG files exist in `src/assets/icons/`
|
|
981
|
+
4. **Logos checked first** - Existing images verified before export
|
|
982
|
+
5. **Logos location** - All logos/images in `public/images/` (not `src/`)
|
|
983
|
+
6. **Image constants** - Centralized `src/config/images.ts` exists and is used
|
|
984
|
+
7. **Image organization** - Images categorized (LOGOS, AUTH, WEBSITE, SERVICES)
|
|
985
|
+
8. **No hardcoded paths** - All images referenced via `IMAGES.CATEGORY.NAME`
|
|
986
|
+
9. **Image format** - SVG used for logos when possible (not PNG)
|
|
987
|
+
|
|
988
|
+
### Colors & Theme
|
|
989
|
+
1. **Theme colors used** - `bg-primary`, `text-primary` instead of hex values
|
|
990
|
+
2. **Primary shades** - Correct use of primary-50 to primary-900
|
|
991
|
+
3. **Semantic colors** - Using HeroUI semantic colors (success, danger, warning)
|
|
992
|
+
|
|
993
|
+
### Components & Styling
|
|
994
|
+
1. **HeroUI components first** - Verify HeroUI library was checked before custom
|
|
995
|
+
2. **No direct HeroUI imports** - Only through `@/components/ui`
|
|
996
|
+
3. **Component configuration** - Using `extendVariants` with `defaultVariants`
|
|
997
|
+
4. **Clean component usage** - No repetitive className props
|
|
998
|
+
5. **Use HeroUI components** - No HTML elements when HeroUI component exists
|
|
999
|
+
6. **Standard Tailwind classes** - No arbitrary values when standard class exists
|
|
1000
|
+
7. **Map from constants** - All menu items, dropdown options mapped from config
|
|
1001
|
+
|
|
1002
|
+
### Navigation
|
|
1003
|
+
1. **Next.js Link** - Using `Link` from `next/link` (not HeroUI Link)
|
|
1004
|
+
2. **Navigation hooks** - Using `usePathname`, `useRouter` from `next/navigation`
|
|
1005
|
+
3. **HeroUI Navbar** - Using HeroUI Navbar components for main navigation
|
|
1006
|
+
4. **Navigation constants** - Items defined in `src/config/navigationItems.ts`
|
|
1007
|
+
5. **Responsive menu** - NavbarMenuToggle and NavbarMenu implemented
|
|
1008
|
+
|
|
1009
|
+
### i18n & Content
|
|
1010
|
+
1. **Zero hardcoded text** - ALL text uses `t('key')`
|
|
1011
|
+
2. **Zero hardcoded URLs** - ALL links use `NAVIGATION_URLS`
|
|
1012
|
+
3. **Form fields i18n** - Labels, placeholders, errors all use translation keys
|
|
600
1013
|
|
|
601
|
-
###
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
11. **Mobile-first** - Base styles for mobile, then `md:`, `lg:`, etc.
|
|
1014
|
+
### Structure & Types
|
|
1015
|
+
1. **Proper TypeScript** - No `any`, all props typed
|
|
1016
|
+
2. **Type safety** - `as const` used for constants
|
|
1017
|
+
3. **Client components** - `'use client'` where needed
|
|
606
1018
|
|
|
607
|
-
###
|
|
608
|
-
|
|
609
|
-
|
|
1019
|
+
### Responsive & UX
|
|
1020
|
+
1. **Mobile-first** - Base styles for mobile, then `md:`, `lg:`, etc.
|
|
1021
|
+
2. **Loading states** - Proper loading indicators
|
|
1022
|
+
3. **Error states** - Proper error handling and display
|
|
610
1023
|
|
|
611
1024
|
---
|
|
612
1025
|
|
|
613
|
-
##
|
|
1026
|
+
## 🚀 Quick Reference Guide
|
|
1027
|
+
|
|
1028
|
+
### Figma MCP Workflow
|
|
1029
|
+
```
|
|
1030
|
+
1. Receive Figma URL from user
|
|
1031
|
+
2. Use Figma MCP server to fetch design
|
|
1032
|
+
3. Analyze pages, frames, components
|
|
1033
|
+
4. Extract design tokens and assets
|
|
1034
|
+
5. Map components to HeroUI
|
|
1035
|
+
6. Implement with proper patterns
|
|
1036
|
+
```
|
|
1037
|
+
|
|
1038
|
+
### Icons Workflow
|
|
1039
|
+
```bash
|
|
1040
|
+
# 1. Export SVG from Figma
|
|
1041
|
+
# 2. Place in folder
|
|
1042
|
+
mv icon.svg src/assets/icons/
|
|
1043
|
+
|
|
1044
|
+
# 3. Generate components
|
|
1045
|
+
pnpm generate-icons
|
|
614
1046
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
1047
|
+
# 4. Use Icon component
|
|
1048
|
+
import { Icon } from '@/components/icons/Icon';
|
|
1049
|
+
<Icon name="Menu" className="size-6" />
|
|
1050
|
+
|
|
1051
|
+
# OR use direct import
|
|
1052
|
+
import { MenuIcon } from '@/components/icons';
|
|
1053
|
+
<MenuIcon className="size-6" />
|
|
1054
|
+
```
|
|
1055
|
+
|
|
1056
|
+
### Logos/Images Workflow
|
|
1057
|
+
```bash
|
|
1058
|
+
# 1. Check if exists first
|
|
1059
|
+
# Check src/config/images.ts
|
|
1060
|
+
|
|
1061
|
+
# 2. If doesn't exist, export from Figma
|
|
1062
|
+
# Export as SVG (preferred) or PNG
|
|
1063
|
+
|
|
1064
|
+
# 3. Place in public with organized folders
|
|
1065
|
+
mv logo.svg public/images/logos/
|
|
1066
|
+
mv hero-bg.webp public/images/website/
|
|
1067
|
+
|
|
1068
|
+
# 4. Update centralized constants file
|
|
1069
|
+
# Edit src/config/images.ts
|
|
1070
|
+
|
|
1071
|
+
const LOGOS = {
|
|
1072
|
+
PRIMARY: '/images/logos/logo-primary.svg',
|
|
1073
|
+
ICON: '/images/logos/logo-icon.svg',
|
|
1074
|
+
} as const;
|
|
1075
|
+
|
|
1076
|
+
const WEBSITE = {
|
|
1077
|
+
HOME_HERO: '/images/website/home-hero.webp',
|
|
1078
|
+
} as const;
|
|
1079
|
+
|
|
1080
|
+
export const IMAGES = {
|
|
1081
|
+
LOGOS,
|
|
1082
|
+
WEBSITE,
|
|
1083
|
+
} as const;
|
|
1084
|
+
|
|
1085
|
+
# 5. Use in code
|
|
1086
|
+
import { IMAGES } from '@/config/images';
|
|
1087
|
+
<img src={IMAGES.LOGOS.PRIMARY} alt="Logo" className="h-10 w-auto" />
|
|
1088
|
+
```
|
|
1089
|
+
|
|
1090
|
+
### Component Configuration Pattern
|
|
1091
|
+
```tsx
|
|
1092
|
+
// File: src/components/ui/Button.tsx
|
|
1093
|
+
import { extendVariants, Button as HeroUIButton } from '@heroui/react';
|
|
1094
|
+
|
|
1095
|
+
export const Button = extendVariants(HeroUIButton, {
|
|
1096
|
+
variants: {
|
|
1097
|
+
color: {
|
|
1098
|
+
primary: 'bg-primary-600 text-black hover:bg-primary-500',
|
|
1099
|
+
},
|
|
1100
|
+
size: {
|
|
1101
|
+
md: 'h-10 px-4 text-sm',
|
|
1102
|
+
},
|
|
1103
|
+
},
|
|
1104
|
+
defaultVariants: {
|
|
1105
|
+
color: 'primary',
|
|
1106
|
+
size: 'md',
|
|
1107
|
+
radius: 'full',
|
|
1108
|
+
},
|
|
1109
|
+
});
|
|
1110
|
+
|
|
1111
|
+
// Usage - Clean and simple:
|
|
1112
|
+
<Button>Click Me</Button>
|
|
1113
|
+
<Button color="secondary" size="lg">Large</Button>
|
|
1114
|
+
```
|
|
1115
|
+
|
|
1116
|
+
### Navigation with HeroUI Navbar
|
|
1117
|
+
```tsx
|
|
1118
|
+
import { Navbar, NavbarBrand, NavbarContent, NavbarItem } from '@heroui/react';
|
|
1119
|
+
import Link from 'next/link';
|
|
1120
|
+
import { NAVIGATION_URLS } from '@/config/navigationUrls';
|
|
1121
|
+
import { IMAGES } from '@/config/images';
|
|
1122
|
+
import { mainNavigationItems } from '@/config/navigationItems';
|
|
1123
|
+
|
|
1124
|
+
<Navbar>
|
|
1125
|
+
<NavbarBrand>
|
|
1126
|
+
<Link href={NAVIGATION_URLS.ROOT}>
|
|
1127
|
+
<img src={IMAGES.LOGOS.PRIMARY} alt={t('logoAlt')} className="h-10" />
|
|
1128
|
+
</Link>
|
|
1129
|
+
</NavbarBrand>
|
|
1130
|
+
|
|
1131
|
+
<NavbarContent>
|
|
1132
|
+
{mainNavigationItems.map((item) => (
|
|
1133
|
+
<NavbarItem key={item.key}>
|
|
1134
|
+
<Link href={item.href}>{t(item.labelKey)}</Link>
|
|
1135
|
+
</NavbarItem>
|
|
1136
|
+
))}
|
|
1137
|
+
</NavbarContent>
|
|
1138
|
+
</Navbar>
|
|
1139
|
+
```
|
|
1140
|
+
|
|
1141
|
+
### Tailwind Standard Classes
|
|
1142
|
+
```tsx
|
|
1143
|
+
// Heights/Widths
|
|
1144
|
+
h-10 = 40px w-14 = 56px size-5 = 20px
|
|
1145
|
+
h-12 = 48px w-20 = 80px size-6 = 24px
|
|
1146
|
+
|
|
1147
|
+
// Spacing
|
|
1148
|
+
p-6 = 24px gap-3 = 12px m-4 = 16px
|
|
1149
|
+
p-8 = 32px gap-4 = 16px m-8 = 32px
|
|
1150
|
+
|
|
1151
|
+
// Text
|
|
1152
|
+
text-sm = 14px text-xl = 20px
|
|
1153
|
+
text-base = 16px text-2xl = 24px
|
|
1154
|
+
text-lg = 18px text-3xl = 30px
|
|
1155
|
+
|
|
1156
|
+
// Use standard class if within 2-4px of target
|
|
1157
|
+
```
|
|
1158
|
+
|
|
1159
|
+
### Primary Colors Usage
|
|
1160
|
+
```tsx
|
|
1161
|
+
// Use theme primary colors (from hero.ts)
|
|
1162
|
+
<Button color="primary"> {/* Uses primary-600 (#25C780) */}
|
|
1163
|
+
<div className="bg-primary"> {/* DEFAULT = #25C780 */}
|
|
1164
|
+
<div className="bg-primary-50"> {/* Lightest shade */}
|
|
1165
|
+
<div className="bg-primary-600"> {/* Same as DEFAULT */}
|
|
1166
|
+
<div className="bg-primary-900"> {/* Darkest shade */}
|
|
1167
|
+
|
|
1168
|
+
// Text colors
|
|
1169
|
+
<p className="text-primary">
|
|
1170
|
+
<p className="text-primary-700">
|
|
1171
|
+
```
|
|
621
1172
|
|
|
622
1173
|
---
|
|
623
1174
|
|
|
624
|
-
**Remember**: This project has a well-defined structure and
|
|
1175
|
+
**Remember**: This project has a well-defined structure, component library, and strict patterns. Your job is to use them correctly by:
|
|
1176
|
+
1. **Always using Figma MCP** to fetch design details
|
|
1177
|
+
2. **Following the Icon component pattern** for cleaner code
|
|
1178
|
+
3. **Creating and maintaining centralized image constants**
|
|
1179
|
+
4. **Checking existing images before exporting** from Figma
|
|
1180
|
+
5. **Using extendVariants with defaultVariants** for component configuration
|
|
1181
|
+
6. **Using theme colors** (primary-600, primary-50, etc.) instead of hardcoded values
|
|
1182
|
+
7. **Implementing HeroUI Navbar** for navigation components
|
|
1183
|
+
8. **Mapping all data from constants** - never hardcode
|
|
1184
|
+
|
|
1185
|
+
Stay within the boundaries of the existing architecture.
|