@adlas/create-app 1.0.48 → 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,6 +4,74 @@
|
|
|
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
|
|
@@ -11,7 +79,7 @@ This document provides comprehensive guidelines for converting Figma designs to
|
|
|
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
81
|
5. **Icons workflow** - Export SVG from Figma → Place in `src/assets/icons/` → Run `pnpm generate-icons` → Use generated TSX components
|
|
14
|
-
6. **Logos workflow** - Export SVG/PNG from Figma → Place in `public/` →
|
|
82
|
+
6. **Logos workflow** - Export SVG/PNG from Figma → Place in `public/images/` → Create centralized constants → Reference via `IMAGES.CATEGORY.NAME`
|
|
15
83
|
7. **Use Tailwind standard classes** - Use closest Tailwind class instead of custom values (e.g., `h-10` not `h-[41px]`)
|
|
16
84
|
8. **Follow project structure** - Place files in correct locations as defined below
|
|
17
85
|
9. **Match coding patterns** - Use the same patterns found in existing code
|
|
@@ -34,37 +102,72 @@ This document provides comprehensive guidelines for converting Figma designs to
|
|
|
34
102
|
<button onClick={handler}>Click</button>
|
|
35
103
|
```
|
|
36
104
|
|
|
37
|
-
**✅ CORRECT - Configure in component wrapper
|
|
105
|
+
**✅ CORRECT - Configure in component wrapper with defaultVariants**:
|
|
38
106
|
```tsx
|
|
39
|
-
// Option 1: If this is a reusable button style, extend Button component
|
|
40
107
|
// File: src/components/ui/Button.tsx
|
|
41
108
|
import { extendVariants, Button as HeroUIButton } from '@heroui/react';
|
|
42
109
|
|
|
43
110
|
export const Button = extendVariants(HeroUIButton, {
|
|
44
111
|
variants: {
|
|
45
112
|
color: {
|
|
46
|
-
primary: 'bg-
|
|
113
|
+
primary: 'bg-primary-600 text-black hover:bg-primary-500',
|
|
114
|
+
secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300',
|
|
47
115
|
},
|
|
48
116
|
size: {
|
|
117
|
+
sm: 'h-8 px-3 text-xs',
|
|
49
118
|
md: 'h-10 px-4 text-sm',
|
|
119
|
+
lg: 'h-12 px-6 text-base',
|
|
50
120
|
},
|
|
51
121
|
},
|
|
122
|
+
defaultVariants: {
|
|
123
|
+
color: 'primary',
|
|
124
|
+
size: 'md',
|
|
125
|
+
radius: 'full',
|
|
126
|
+
},
|
|
52
127
|
});
|
|
53
128
|
|
|
54
|
-
// Usage in code:
|
|
55
|
-
<Button
|
|
56
|
-
|
|
57
|
-
</Button>
|
|
58
|
-
|
|
59
|
-
// Option 2: For one-off usage, use HeroUI Button with minimal custom classes
|
|
60
|
-
import { Button } from '@/components/ui';
|
|
61
|
-
<Button color="primary" radius="full">Click Me</Button>
|
|
129
|
+
// Usage in code - Clean and declarative:
|
|
130
|
+
<Button>Click Me</Button> // Uses defaultVariants
|
|
131
|
+
<Button color="secondary" size="lg">Large Button</Button>
|
|
62
132
|
```
|
|
63
133
|
|
|
64
134
|
**When to configure vs use inline**:
|
|
65
|
-
- ✅ Configure: Repeated styles, component variants, brand colors
|
|
135
|
+
- ✅ Configure with defaultVariants: Repeated styles, component variants, brand colors, default radius
|
|
66
136
|
- ✅ Inline: Layout utilities only (`mt-4`, `hidden`, `lg:block`)
|
|
67
|
-
- ❌ Never inline: Component-specific styles (heights, colors, padding, text sizes)
|
|
137
|
+
- ❌ Never inline: Component-specific styles (heights, colors, padding, text sizes, border radius)
|
|
138
|
+
|
|
139
|
+
### extendVariants Pattern
|
|
140
|
+
|
|
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
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
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
|
|
68
171
|
|
|
69
172
|
### Data Must Be Mapped from Constants
|
|
70
173
|
|
|
@@ -81,13 +184,16 @@ import { Button } from '@/components/ui';
|
|
|
81
184
|
```tsx
|
|
82
185
|
// File: src/config/menuOptions.ts
|
|
83
186
|
export const productMenuItems = [
|
|
84
|
-
{ key: 'analytics', labelKey: 'product.analytics', href:
|
|
85
|
-
{ key: 'insights', labelKey: 'product.insights', href:
|
|
86
|
-
{ key: 'reports', labelKey: 'product.reports', href:
|
|
87
|
-
];
|
|
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;
|
|
88
191
|
|
|
89
192
|
// Usage:
|
|
90
193
|
import { productMenuItems } from '@/config/menuOptions';
|
|
194
|
+
import { useTranslations } from 'next-intl';
|
|
195
|
+
|
|
196
|
+
const t = useTranslations();
|
|
91
197
|
|
|
92
198
|
<DropdownMenu>
|
|
93
199
|
{productMenuItems.map((item) => (
|
|
@@ -100,42 +206,138 @@ import { productMenuItems } from '@/config/menuOptions';
|
|
|
100
206
|
|
|
101
207
|
---
|
|
102
208
|
|
|
103
|
-
## 🎨
|
|
209
|
+
## 🎨 Color Palette & Theme
|
|
104
210
|
|
|
105
|
-
###
|
|
106
|
-
Before creating ANY component, check if HeroUI provides it:
|
|
107
|
-
- **HeroUI Documentation**: https://heroui.com/docs/components
|
|
108
|
-
- **HeroUI Figma Kit**: https://www.figma.com/design/kFGcjHsNKZx7zh2NxEJXYt/HeroUI-Figma-Kit--Community---Community-
|
|
211
|
+
### Primary Colors (from hero.ts)
|
|
109
212
|
|
|
110
|
-
|
|
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
|
+
}
|
|
111
229
|
```
|
|
112
|
-
1. Does HeroUI have this component?
|
|
113
|
-
→ YES: Use HeroUI component (via @/components/ui wrapper if exists)
|
|
114
|
-
→ NO: Go to step 2
|
|
115
230
|
|
|
116
|
-
|
|
117
|
-
→ YES: Use the wrapper
|
|
118
|
-
→ NO: Go to step 3
|
|
231
|
+
### Using Primary Colors in Code
|
|
119
232
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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 */}
|
|
123
250
|
```
|
|
124
251
|
|
|
125
|
-
###
|
|
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
|
+
|
|
126
270
|
```tsx
|
|
127
|
-
|
|
128
|
-
|
|
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';
|
|
129
278
|
|
|
130
|
-
//
|
|
131
|
-
|
|
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();
|
|
132
290
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
<
|
|
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
|
+
}
|
|
137
331
|
```
|
|
138
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
|
+
|
|
139
341
|
---
|
|
140
342
|
|
|
141
343
|
## 📁 Project Structure
|
|
@@ -147,187 +349,103 @@ import { Input } from '@/components/ui'; // ✅ Use HeroUI Input wrapper
|
|
|
147
349
|
### Components Location
|
|
148
350
|
```
|
|
149
351
|
src/components/
|
|
150
|
-
├── ui/ # Reusable UI components (HeroUI wrappers)
|
|
151
|
-
├── icons/ #
|
|
352
|
+
├── ui/ # Reusable UI components (HeroUI wrappers with extendVariants)
|
|
353
|
+
├── icons/ # Auto-generated icon components (DO NOT manually edit)
|
|
152
354
|
├── layout/ # Layout components (Navbar, Footer)
|
|
153
355
|
└── [feature]/ # Feature-specific components
|
|
154
356
|
```
|
|
155
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
|
+
|
|
156
367
|
### Styling
|
|
157
368
|
- **Global styles**: `src/styles/globals.css`
|
|
158
|
-
- **Theme config**: `src/styles/hero.ts`
|
|
369
|
+
- **Theme config**: `src/styles/hero.ts` (Primary colors defined here)
|
|
159
370
|
- **Approach**: Tailwind CSS utility classes ONLY
|
|
160
371
|
- **NO CSS modules, NO styled-components, NO inline styles**
|
|
161
372
|
|
|
162
373
|
---
|
|
163
374
|
|
|
164
|
-
##
|
|
375
|
+
## 🖼️ Icons
|
|
165
376
|
|
|
166
|
-
###
|
|
167
|
-
All located in `src/components/ui/`:
|
|
377
|
+
### CRITICAL WORKFLOW: Icon Component Pattern
|
|
168
378
|
|
|
169
|
-
|
|
170
|
-
```tsx
|
|
171
|
-
import { Input, PasswordInput, NumberInput, Textarea } from '@/components/ui';
|
|
172
|
-
|
|
173
|
-
// Text Input
|
|
174
|
-
<Input
|
|
175
|
-
label="Email"
|
|
176
|
-
placeholder="Enter your email"
|
|
177
|
-
type="email"
|
|
178
|
-
isRequired
|
|
179
|
-
/>
|
|
180
|
-
|
|
181
|
-
// Password Input (with show/hide toggle)
|
|
182
|
-
<PasswordInput
|
|
183
|
-
label="Password"
|
|
184
|
-
placeholder="Enter password"
|
|
185
|
-
isRequired
|
|
186
|
-
/>
|
|
187
|
-
|
|
188
|
-
// Number Input (with increment/decrement)
|
|
189
|
-
<NumberInput
|
|
190
|
-
label="Quantity"
|
|
191
|
-
min={1}
|
|
192
|
-
max={100}
|
|
193
|
-
defaultValue={1}
|
|
194
|
-
/>
|
|
379
|
+
**Modern Approach**: Use a centralized `Icon` component that dynamically renders icons:
|
|
195
380
|
|
|
196
|
-
// Textarea
|
|
197
|
-
<Textarea
|
|
198
|
-
label="Description"
|
|
199
|
-
placeholder="Enter description"
|
|
200
|
-
rows={4}
|
|
201
|
-
/>
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
#### Selection Components
|
|
205
381
|
```tsx
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
<Select
|
|
210
|
-
label="Country"
|
|
211
|
-
placeholder="Select country"
|
|
212
|
-
items={countries}
|
|
213
|
-
/>
|
|
214
|
-
|
|
215
|
-
// Radio Group
|
|
216
|
-
<RadioGroup
|
|
217
|
-
label="Size"
|
|
218
|
-
options={[
|
|
219
|
-
{ value: 's', label: 'Small' },
|
|
220
|
-
{ value: 'm', label: 'Medium' },
|
|
221
|
-
{ value: 'l', label: 'Large' },
|
|
222
|
-
]}
|
|
223
|
-
/>
|
|
382
|
+
// File: src/components/icons/Icon.tsx
|
|
383
|
+
import dynamic from 'next/dynamic';
|
|
384
|
+
import type { SVGProps } from 'react';
|
|
224
385
|
|
|
225
|
-
|
|
226
|
-
|
|
386
|
+
interface IconProps extends SVGProps<SVGSVGElement> {
|
|
387
|
+
name: string; // Icon name without "Icon" suffix
|
|
388
|
+
}
|
|
227
389
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
placeholder="Type to search..."
|
|
233
|
-
/>
|
|
234
|
-
```
|
|
390
|
+
export function Icon({ name, ...props }: IconProps) {
|
|
391
|
+
const IconComponent = dynamic(() =>
|
|
392
|
+
import(`./index`).then((mod) => mod[`${name}Icon`])
|
|
393
|
+
);
|
|
235
394
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
import { DatePicker } from '@/components/ui';
|
|
395
|
+
return <IconComponent {...props} />;
|
|
396
|
+
}
|
|
239
397
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
/>
|
|
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" />
|
|
244
402
|
```
|
|
245
403
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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
|
|
249
409
|
|
|
250
|
-
|
|
251
|
-
Submit
|
|
252
|
-
</Button>
|
|
410
|
+
### Icon Generation Workflow
|
|
253
411
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
412
|
+
1. **Export SVG from Figma**
|
|
413
|
+
- Select icon in Figma
|
|
414
|
+
- Export as SVG (not PNG/JPG)
|
|
415
|
+
- Download the SVG file
|
|
257
416
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
```
|
|
417
|
+
2. **Place in Icons Folder**
|
|
418
|
+
```bash
|
|
419
|
+
mv downloaded-icon.svg src/assets/icons/icon-name.svg
|
|
420
|
+
```
|
|
262
421
|
|
|
263
|
-
|
|
264
|
-
```
|
|
265
|
-
|
|
422
|
+
3. **Generate React Components**
|
|
423
|
+
```bash
|
|
424
|
+
pnpm generate-icons
|
|
425
|
+
```
|
|
266
426
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
>
|
|
273
|
-
Modal content
|
|
274
|
-
</Modal>
|
|
275
|
-
|
|
276
|
-
// Chip (badges/tags)
|
|
277
|
-
<Chip color="success" variant="flat">
|
|
278
|
-
Active
|
|
279
|
-
</Chip>
|
|
280
|
-
|
|
281
|
-
// Breadcrumbs
|
|
282
|
-
<Breadcrumbs
|
|
283
|
-
items={[
|
|
284
|
-
{ label: 'Home', href: '/' },
|
|
285
|
-
{ label: 'Products', href: '/products' },
|
|
286
|
-
]}
|
|
287
|
-
/>
|
|
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
|
|
288
432
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
{ key: 'tab1', label: 'Tab 1', content: <div>Content 1</div> },
|
|
293
|
-
{ key: 'tab2', label: 'Tab 2', content: <div>Content 2</div> },
|
|
294
|
-
]}
|
|
295
|
-
/>
|
|
296
|
-
```
|
|
433
|
+
4. **Use with Icon Component**
|
|
434
|
+
```tsx
|
|
435
|
+
import { Icon } from '@/components/icons/Icon';
|
|
297
436
|
|
|
298
|
-
|
|
437
|
+
<Icon name="ChevronDown" className="size-5 text-white" />
|
|
438
|
+
```
|
|
299
439
|
|
|
300
|
-
|
|
440
|
+
### Alternative: Direct Import (When Needed)
|
|
301
441
|
|
|
302
|
-
|
|
303
|
-
1. Select the icon in Figma design
|
|
304
|
-
2. Export as SVG (not PNG/JPG)
|
|
305
|
-
3. Download the SVG file
|
|
442
|
+
If you need direct imports for tree-shaking or specific use cases:
|
|
306
443
|
|
|
307
|
-
#### Step 2: Place in Icons Folder
|
|
308
|
-
```bash
|
|
309
|
-
# Place SVG file in src/assets/icons/
|
|
310
|
-
mv downloaded-icon.svg src/assets/icons/icon-name.svg
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
#### Step 3: Generate React Components
|
|
314
|
-
```bash
|
|
315
|
-
# Run the icon generation script
|
|
316
|
-
pnpm generate-icons
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
This command will:
|
|
320
|
-
- Convert all SVG files in `src/assets/icons/` to React components
|
|
321
|
-
- Place them in `src/components/icons/`
|
|
322
|
-
- Auto-format and lint the generated code
|
|
323
|
-
- Update `src/components/icons/index.ts` with exports
|
|
324
|
-
|
|
325
|
-
#### Step 4: Use in Code
|
|
326
444
|
```tsx
|
|
327
|
-
import {
|
|
445
|
+
import { ChevronDownIcon, MenuIcon } from '@/components/icons';
|
|
328
446
|
|
|
329
447
|
<ChevronDownIcon className="size-5 text-white" />
|
|
330
|
-
<
|
|
448
|
+
<MenuIcon className="size-6" />
|
|
331
449
|
```
|
|
332
450
|
|
|
333
451
|
**Available Icons** (auto-generated):
|
|
@@ -344,45 +462,77 @@ import { IconNameIcon, ChevronDownIcon } from '@/components/icons';
|
|
|
344
462
|
- Create manual icon components
|
|
345
463
|
- Use `<img>` tags for icons
|
|
346
464
|
- Hardcode SVG paths directly in components
|
|
465
|
+
- Edit files in `src/components/icons/` manually (they are auto-generated)
|
|
347
466
|
|
|
348
467
|
**✅ DO**:
|
|
349
468
|
- Always export from Figma as SVG
|
|
350
469
|
- Use `pnpm generate-icons` to create components
|
|
351
|
-
-
|
|
470
|
+
- Use `<Icon name="IconName" />` pattern for cleaner code
|
|
352
471
|
- Use Tailwind classes for sizing (`size-4`, `size-5`, `size-6`)
|
|
472
|
+
- Organize icons in `src/assets/icons/` with descriptive names
|
|
473
|
+
|
|
474
|
+
---
|
|
353
475
|
|
|
354
|
-
|
|
476
|
+
## 🖼️ Logos & Images
|
|
355
477
|
|
|
356
|
-
|
|
478
|
+
### CRITICAL WORKFLOW: Centralized Image Constants
|
|
479
|
+
|
|
480
|
+
**Step 1: Export from Figma**
|
|
357
481
|
|
|
358
|
-
#### Step 1: Export from Figma
|
|
359
482
|
1. **For logos**: Export as SVG (preferred) or PNG if source is raster
|
|
360
483
|
2. **For images**: Export as PNG, JPG, or WebP based on image type
|
|
361
484
|
3. Use appropriate resolution (2x or 3x for retina displays)
|
|
362
485
|
|
|
363
|
-
|
|
486
|
+
**Step 2: Organize in Public Folder**
|
|
487
|
+
|
|
364
488
|
```bash
|
|
365
|
-
# Place logo/image files in public/
|
|
366
489
|
public/
|
|
367
|
-
├── logo.svg # Main logo (SVG preferred)
|
|
368
|
-
├── logo-dark.svg # Dark mode logo
|
|
369
|
-
├── logo.png # Fallback PNG
|
|
370
|
-
├── favicon.ico
|
|
371
490
|
└── images/
|
|
372
|
-
├──
|
|
373
|
-
|
|
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
|
|
374
514
|
```
|
|
375
515
|
|
|
376
|
-
|
|
516
|
+
**Step 3: Create Centralized Image Constants**
|
|
377
517
|
|
|
378
|
-
**CRITICAL**:
|
|
518
|
+
**CRITICAL**: Always create and maintain `src/config/images.ts`:
|
|
379
519
|
|
|
380
520
|
```typescript
|
|
381
521
|
// src/config/images.ts
|
|
382
522
|
|
|
383
523
|
/**
|
|
384
524
|
* Centralized image path constants
|
|
385
|
-
*
|
|
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" />
|
|
386
536
|
*/
|
|
387
537
|
|
|
388
538
|
const LOGOS = {
|
|
@@ -427,14 +577,13 @@ export const IMAGES = {
|
|
|
427
577
|
} as const;
|
|
428
578
|
```
|
|
429
579
|
|
|
430
|
-
|
|
580
|
+
**Step 4: Reference in Components**
|
|
431
581
|
|
|
432
582
|
```tsx
|
|
433
|
-
// Import from centralized constants
|
|
434
583
|
import { IMAGES } from '@/config/images';
|
|
435
584
|
import Image from 'next/image';
|
|
436
585
|
|
|
437
|
-
// Using Next.js Image component (recommended)
|
|
586
|
+
// ✅ Using Next.js Image component (recommended for optimization)
|
|
438
587
|
<Image
|
|
439
588
|
src={IMAGES.LOGOS.FULL}
|
|
440
589
|
alt="Company Logo"
|
|
@@ -443,40 +592,82 @@ import Image from 'next/image';
|
|
|
443
592
|
priority
|
|
444
593
|
/>
|
|
445
594
|
|
|
446
|
-
// Using standard img tag (for SVGs with CSS control)
|
|
595
|
+
// ✅ Using standard img tag (for SVGs with CSS control)
|
|
447
596
|
<img src={IMAGES.LOGOS.PRIMARY_CIRCLE} alt="Logo" className="h-10 w-auto" />
|
|
448
597
|
|
|
449
|
-
// Background images via Tailwind
|
|
450
|
-
<div
|
|
598
|
+
// ✅ Background images via Tailwind
|
|
599
|
+
<div
|
|
600
|
+
className="bg-cover bg-center"
|
|
601
|
+
style={{ backgroundImage: `url(${IMAGES.WEBSITE.HOME_HERO})` }}
|
|
602
|
+
>
|
|
451
603
|
|
|
452
|
-
// In authentication pages
|
|
604
|
+
// ✅ In authentication pages
|
|
453
605
|
<Image
|
|
454
606
|
src={IMAGES.AUTH.LOGIN_BG}
|
|
455
607
|
alt="Login background"
|
|
456
608
|
fill
|
|
457
609
|
className="object-cover"
|
|
458
610
|
/>
|
|
611
|
+
|
|
612
|
+
// ✅ In service pages
|
|
613
|
+
<Image
|
|
614
|
+
src={IMAGES.SERVICES.HEADER}
|
|
615
|
+
alt="Services"
|
|
616
|
+
width={1920}
|
|
617
|
+
height={600}
|
|
618
|
+
className="w-full"
|
|
619
|
+
/>
|
|
459
620
|
```
|
|
460
621
|
|
|
461
|
-
|
|
622
|
+
### Image Format Preference Order
|
|
623
|
+
|
|
462
624
|
1. ✅ **SVG** - Best for logos, icons, and vector graphics (scalable, small file size)
|
|
463
|
-
2. ✅ **WebP** - Modern format for photos (
|
|
625
|
+
2. ✅ **WebP** - Modern format for photos (better compression than PNG/JPG)
|
|
464
626
|
3. ✅ **PNG** - For images requiring transparency
|
|
465
627
|
4. ✅ **JPG** - For photos without transparency
|
|
466
628
|
|
|
629
|
+
### CRITICAL: Check Before Export Workflow
|
|
630
|
+
|
|
631
|
+
Before exporting any logo or image from Figma:
|
|
632
|
+
|
|
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
|
|
637
|
+
|
|
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
|
+
|
|
467
656
|
**❌ DO NOT**:
|
|
468
657
|
- Put images in `src/` folder
|
|
469
658
|
- Import images as modules unless necessary
|
|
470
659
|
- Use base64 encoded images inline
|
|
471
660
|
- Forget to optimize images before adding
|
|
472
|
-
- Hardcode image paths directly in components (
|
|
661
|
+
- **Hardcode image paths directly in components** (CRITICAL ERROR)
|
|
662
|
+
- Export images that already exist in the project
|
|
473
663
|
|
|
474
664
|
**✅ DO**:
|
|
475
|
-
- Always place in `public/`
|
|
476
|
-
- Create
|
|
477
|
-
- Organize image paths by category (LOGOS, AUTH, WEBSITE, SERVICES)
|
|
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)
|
|
478
668
|
- Use `as const` for type safety
|
|
479
|
-
- Import from `@/config/images` in components
|
|
669
|
+
- Import from `@/config/images` in ALL components
|
|
670
|
+
- Check if image exists before exporting from Figma
|
|
480
671
|
- Prefer SVG for logos and icons
|
|
481
672
|
- Optimize images before adding (use tools like ImageOptim, Squoosh)
|
|
482
673
|
- Use Next.js `<Image>` component for automatic optimization
|
|
@@ -532,18 +723,6 @@ auto, full, screen, fit, min, max
|
|
|
532
723
|
- No standard Tailwind class is close enough
|
|
533
724
|
- Using design tokens/CSS variables: `bg-[var(--custom-color)]`
|
|
534
725
|
|
|
535
|
-
**Examples**:
|
|
536
|
-
```tsx
|
|
537
|
-
// ✅ Good - Using standard classes
|
|
538
|
-
<div className="h-10 w-full p-6 text-sm">
|
|
539
|
-
|
|
540
|
-
// ✅ Acceptable - Design tokens
|
|
541
|
-
<div className="bg-[var(--colors/primary)] text-[#19ffa3]">
|
|
542
|
-
|
|
543
|
-
// ❌ Bad - Should use standard classes
|
|
544
|
-
<div className="h-[40px] w-[100%] p-[24px] text-[14px]">
|
|
545
|
-
```
|
|
546
|
-
|
|
547
726
|
### Responsive Breakpoints (Mobile-First)
|
|
548
727
|
```css
|
|
549
728
|
/* Mobile */
|
|
@@ -564,108 +743,19 @@ xl: 1280px /* MacBook Air */
|
|
|
564
743
|
4xl: 1728px /* MacBook Pro 16" */
|
|
565
744
|
```
|
|
566
745
|
|
|
567
|
-
### Usage Example
|
|
568
|
-
```tsx
|
|
569
|
-
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
570
|
-
{/* Mobile: 1 column, Tablet: 2 columns, Desktop: 3 columns */}
|
|
571
|
-
</div>
|
|
572
|
-
```
|
|
573
|
-
|
|
574
746
|
### HeroUI Color System
|
|
747
|
+
|
|
575
748
|
Use these semantic colors from HeroUI theme:
|
|
576
749
|
- `bg-background` - Main background
|
|
577
750
|
- `bg-foreground` - Text color background
|
|
578
751
|
- `bg-default` / `bg-default-50` to `bg-default-900` - Gray scale
|
|
579
|
-
- `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
|
|
580
754
|
- `bg-secondary` - Secondary color
|
|
581
755
|
- `bg-success` - Success states
|
|
582
756
|
- `bg-warning` - Warning states
|
|
583
757
|
- `bg-danger` - Error/danger states
|
|
584
758
|
|
|
585
|
-
### Common Patterns
|
|
586
|
-
```tsx
|
|
587
|
-
// Card
|
|
588
|
-
<div className="rounded-lg bg-white p-6 shadow-md">
|
|
589
|
-
|
|
590
|
-
// Container
|
|
591
|
-
<div className="container mx-auto px-4">
|
|
592
|
-
|
|
593
|
-
// Flexbox centering
|
|
594
|
-
<div className="flex items-center justify-center">
|
|
595
|
-
|
|
596
|
-
// Grid layout
|
|
597
|
-
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
598
|
-
|
|
599
|
-
// Spacing
|
|
600
|
-
<div className="space-y-4"> {/* Vertical spacing */}
|
|
601
|
-
<div className="space-x-4"> {/* Horizontal spacing */}
|
|
602
|
-
```
|
|
603
|
-
|
|
604
|
-
---
|
|
605
|
-
|
|
606
|
-
## 📝 Form Implementation with Zod
|
|
607
|
-
|
|
608
|
-
### Pattern to Follow
|
|
609
|
-
```tsx
|
|
610
|
-
'use client';
|
|
611
|
-
|
|
612
|
-
import { zodResolver } from '@hookform/resolvers/zod';
|
|
613
|
-
import { useForm } from 'react-hook-form';
|
|
614
|
-
import { z } from 'zod';
|
|
615
|
-
|
|
616
|
-
import { Button, Input } from '@/components/ui';
|
|
617
|
-
|
|
618
|
-
// 1. Define schema
|
|
619
|
-
const schema = z.object({
|
|
620
|
-
email: z.string().email('Invalid email'),
|
|
621
|
-
password: z.string().min(8, 'Password must be at least 8 characters'),
|
|
622
|
-
});
|
|
623
|
-
|
|
624
|
-
type FormData = z.infer<typeof schema>;
|
|
625
|
-
|
|
626
|
-
export default function LoginForm() {
|
|
627
|
-
// 2. Setup form
|
|
628
|
-
const {
|
|
629
|
-
register,
|
|
630
|
-
handleSubmit,
|
|
631
|
-
formState: { errors, isSubmitting },
|
|
632
|
-
} = useForm<FormData>({
|
|
633
|
-
resolver: zodResolver(schema),
|
|
634
|
-
});
|
|
635
|
-
|
|
636
|
-
// 3. Submit handler
|
|
637
|
-
const onSubmit = async (data: FormData) => {
|
|
638
|
-
// Handle form submission
|
|
639
|
-
console.log(data);
|
|
640
|
-
};
|
|
641
|
-
|
|
642
|
-
// 4. Render form
|
|
643
|
-
return (
|
|
644
|
-
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
|
|
645
|
-
<Input
|
|
646
|
-
label="Email"
|
|
647
|
-
type="email"
|
|
648
|
-
{...register('email')}
|
|
649
|
-
errorMessage={errors.email?.message}
|
|
650
|
-
isInvalid={!!errors.email}
|
|
651
|
-
/>
|
|
652
|
-
|
|
653
|
-
<Input
|
|
654
|
-
label="Password"
|
|
655
|
-
type="password"
|
|
656
|
-
{...register('password')}
|
|
657
|
-
errorMessage={errors.password?.message}
|
|
658
|
-
isInvalid={!!errors.password}
|
|
659
|
-
/>
|
|
660
|
-
|
|
661
|
-
<Button type="submit" isLoading={isSubmitting} className="w-full">
|
|
662
|
-
Submit
|
|
663
|
-
</Button>
|
|
664
|
-
</form>
|
|
665
|
-
);
|
|
666
|
-
}
|
|
667
|
-
```
|
|
668
|
-
|
|
669
759
|
---
|
|
670
760
|
|
|
671
761
|
## 🌐 Internationalization (i18n)
|
|
@@ -702,24 +792,6 @@ import { Link } from '@heroui/react';
|
|
|
702
792
|
<Link href={NAVIGATION_URLS.ABOUT}>About</Link>
|
|
703
793
|
```
|
|
704
794
|
|
|
705
|
-
**Using Navigation Hooks**:
|
|
706
|
-
```tsx
|
|
707
|
-
'use client';
|
|
708
|
-
|
|
709
|
-
import { usePathname, useRouter } from 'next/navigation';
|
|
710
|
-
|
|
711
|
-
export function MyComponent() {
|
|
712
|
-
const pathname = usePathname();
|
|
713
|
-
const router = useRouter();
|
|
714
|
-
|
|
715
|
-
const handleNavigation = () => {
|
|
716
|
-
router.push(NAVIGATION_URLS.DASHBOARD.INDEX);
|
|
717
|
-
};
|
|
718
|
-
|
|
719
|
-
return <button onClick={handleNavigation}>Go to Dashboard</button>;
|
|
720
|
-
}
|
|
721
|
-
```
|
|
722
|
-
|
|
723
795
|
### Translations for ALL text
|
|
724
796
|
```tsx
|
|
725
797
|
import { useTranslations } from 'next-intl';
|
|
@@ -740,173 +812,77 @@ function MyComponent() {
|
|
|
740
812
|
}
|
|
741
813
|
```
|
|
742
814
|
|
|
743
|
-
### Form labels, placeholders, errors - ALL must use i18n
|
|
744
|
-
```tsx
|
|
745
|
-
import { useTranslations } from 'next-intl';
|
|
746
|
-
|
|
747
|
-
function LoginForm() {
|
|
748
|
-
const t = useTranslations();
|
|
749
|
-
|
|
750
|
-
return (
|
|
751
|
-
<form>
|
|
752
|
-
{/* ✅ CORRECT */}
|
|
753
|
-
<Input
|
|
754
|
-
label={t('email')}
|
|
755
|
-
placeholder={t('enterEmail')}
|
|
756
|
-
errorMessage={errors.email?.message ? t(errors.email.message) : undefined}
|
|
757
|
-
/>
|
|
758
|
-
|
|
759
|
-
{/* ❌ WRONG - Hardcoded text */}
|
|
760
|
-
<Input
|
|
761
|
-
label="Email"
|
|
762
|
-
placeholder="Enter your email"
|
|
763
|
-
errorMessage="Invalid email"
|
|
764
|
-
/>
|
|
765
|
-
</form>
|
|
766
|
-
);
|
|
767
|
-
}
|
|
768
|
-
```
|
|
769
|
-
|
|
770
|
-
### Navigation URLs Reference
|
|
771
|
-
```tsx
|
|
772
|
-
import { NAVIGATION_URLS } from '@/config/navigationUrls';
|
|
773
|
-
|
|
774
|
-
// Available URLs (based on project type):
|
|
775
|
-
NAVIGATION_URLS.ROOT // '/'
|
|
776
|
-
NAVIGATION_URLS.ABOUT // '/about'
|
|
777
|
-
NAVIGATION_URLS.CONTACT // '/contact'
|
|
778
|
-
NAVIGATION_URLS.PRODUCTS.INDEX // '/products'
|
|
779
|
-
NAVIGATION_URLS.PRODUCTS.DETAIL('123') // '/products/123'
|
|
780
|
-
NAVIGATION_URLS.AUTH.LOGIN // '/auth/login'
|
|
781
|
-
NAVIGATION_URLS.DASHBOARD.INDEX // '/dashboard'
|
|
782
|
-
|
|
783
|
-
// ✅ ALWAYS use NAVIGATION_URLS
|
|
784
|
-
<Link href={NAVIGATION_URLS.PRODUCTS.INDEX}>Products</Link>
|
|
785
|
-
|
|
786
|
-
// ❌ NEVER hardcode URLs
|
|
787
|
-
<Link href="/products">Products</Link>
|
|
788
|
-
```
|
|
789
|
-
|
|
790
|
-
---
|
|
791
|
-
|
|
792
|
-
## 🎯 Data Fetching
|
|
793
|
-
|
|
794
|
-
### Using React Query
|
|
795
|
-
```tsx
|
|
796
|
-
'use client';
|
|
797
|
-
|
|
798
|
-
import { useQuery } from '@tanstack/react-query';
|
|
799
|
-
import axios from 'axios';
|
|
800
|
-
|
|
801
|
-
import { QUERY_KEYS } from '@/libs/react-query';
|
|
802
|
-
|
|
803
|
-
export default function ProductList() {
|
|
804
|
-
const { data, isLoading, error } = useQuery({
|
|
805
|
-
queryKey: QUERY_KEYS.PRODUCTS.LIST,
|
|
806
|
-
queryFn: async () => {
|
|
807
|
-
const response = await axios.get('/api/products');
|
|
808
|
-
return response.data;
|
|
809
|
-
},
|
|
810
|
-
});
|
|
811
|
-
|
|
812
|
-
if (isLoading) return <div>Loading...</div>;
|
|
813
|
-
if (error) return <div>Error loading products</div>;
|
|
814
|
-
|
|
815
|
-
return (
|
|
816
|
-
<div>
|
|
817
|
-
{data.map((product) => (
|
|
818
|
-
<div key={product.id}>{product.name}</div>
|
|
819
|
-
))}
|
|
820
|
-
</div>
|
|
821
|
-
);
|
|
822
|
-
}
|
|
823
|
-
```
|
|
824
|
-
|
|
825
|
-
---
|
|
826
|
-
|
|
827
|
-
## 📐 Layout Components
|
|
828
|
-
|
|
829
|
-
### Using Existing Layouts
|
|
830
|
-
```tsx
|
|
831
|
-
// Auth pages use auth layout automatically
|
|
832
|
-
// Location: src/app/[locale]/(auth)/login/page.tsx
|
|
833
|
-
|
|
834
|
-
'use client';
|
|
835
|
-
|
|
836
|
-
export default function LoginPage() {
|
|
837
|
-
return (
|
|
838
|
-
<div className="w-full max-w-md space-y-6 rounded-lg bg-white p-8 shadow-md">
|
|
839
|
-
<h1 className="text-center text-2xl font-bold">Login</h1>
|
|
840
|
-
{/* Your form here */}
|
|
841
|
-
</div>
|
|
842
|
-
);
|
|
843
|
-
}
|
|
844
|
-
```
|
|
845
|
-
|
|
846
|
-
### Navbar & Footer
|
|
847
|
-
Already implemented in `src/components/layout/`:
|
|
848
|
-
- `Navbar.tsx` - Main navigation
|
|
849
|
-
- `Footer.tsx` - Footer component
|
|
850
|
-
|
|
851
|
-
Use in root layout:
|
|
852
|
-
```tsx
|
|
853
|
-
import { Footer, Navbar } from '@/components/layout';
|
|
854
|
-
|
|
855
|
-
<Navbar />
|
|
856
|
-
<main>{children}</main>
|
|
857
|
-
<Footer />
|
|
858
|
-
```
|
|
859
|
-
|
|
860
815
|
---
|
|
861
816
|
|
|
862
817
|
## ⚠️ CRITICAL RULES
|
|
863
818
|
|
|
864
819
|
### ❌ DO NOT
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
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)
|
|
885
847
|
|
|
886
848
|
### ✅ DO
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
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
|
|
910
886
|
|
|
911
887
|
---
|
|
912
888
|
|
|
@@ -914,41 +890,65 @@ import { Footer, Navbar } from '@/components/layout';
|
|
|
914
890
|
|
|
915
891
|
When converting Figma to code, ensure:
|
|
916
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
|
+
|
|
917
900
|
### Icons & Assets
|
|
918
901
|
- [ ] Exported icons as SVG from Figma
|
|
919
902
|
- [ ] Placed SVG files in `src/assets/icons/`
|
|
920
903
|
- [ ] Ran `pnpm generate-icons` to create React components
|
|
921
|
-
- [ ]
|
|
922
|
-
- [ ]
|
|
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
|
|
923
907
|
- [ ] Placed logos/images in `public/images/` with organized folders (logos/, auth/, website/, services/)
|
|
924
|
-
- [ ] Created centralized constants file `src/config/images.ts`
|
|
908
|
+
- [ ] Created/updated centralized constants file `src/config/images.ts`
|
|
925
909
|
- [ ] Organized image paths by category (LOGOS, AUTH, WEBSITE, SERVICES, PRODUCTS)
|
|
926
910
|
- [ ] Used `as const` for type safety
|
|
927
|
-
- [ ] Referenced via `IMAGES.
|
|
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
|
|
928
918
|
|
|
929
919
|
### Component & Styling
|
|
930
920
|
- [ ] Checked HeroUI library first for component availability
|
|
931
|
-
- [ ] Used HeroUI components via `src/components/ui/` wrappers
|
|
932
|
-
- [ ]
|
|
921
|
+
- [ ] Used HeroUI components via `src/components/ui/` wrappers
|
|
922
|
+
- [ ] Used `extendVariants` pattern with `defaultVariants` for component configuration
|
|
923
|
+
- [ ] Kept component usage clean (no repetitive className props)
|
|
933
924
|
- [ ] Used only layout utilities inline (`mt-4`, `hidden`, `lg:block`)
|
|
934
925
|
- [ ] Applied Tailwind CSS classes only (no custom CSS, no CSS modules)
|
|
935
|
-
- [ ] Used standard Tailwind classes instead of arbitrary values
|
|
926
|
+
- [ ] Used standard Tailwind classes instead of arbitrary values
|
|
936
927
|
- [ ] Followed HeroUI theme colors (bg-background, bg-primary, etc.)
|
|
937
928
|
- [ ] Mapped all data from constants (no hardcoded menu items/dropdowns)
|
|
929
|
+
- [ ] Created constants files for navigation items and menu options
|
|
938
930
|
|
|
939
|
-
###
|
|
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
|
|
940
939
|
- [ ] ALL text uses i18n translation keys (no hardcoded text)
|
|
941
940
|
- [ ] ALL labels use `t('labelKey')`
|
|
942
941
|
- [ ] ALL placeholders use `t('placeholderKey')`
|
|
943
942
|
- [ ] ALL error messages use `t('errorKey')`
|
|
944
943
|
- [ ] ALL titles and headings use `t('titleKey')`
|
|
945
|
-
- [ ] ALL
|
|
944
|
+
- [ ] ALL button text use `t('buttonKey')`
|
|
946
945
|
|
|
947
946
|
### Structure & Types
|
|
948
947
|
- [ ] Page created in correct location (`src/app/[locale]/...`)
|
|
949
948
|
- [ ] Added TypeScript types for all data structures
|
|
950
949
|
- [ ] Used `'use client'` for interactive components
|
|
951
950
|
- [ ] No `any` types used
|
|
951
|
+
- [ ] Used `as const` for constants type safety
|
|
952
952
|
|
|
953
953
|
### Forms & Validation
|
|
954
954
|
- [ ] Implemented forms with Zod validation schemas
|
|
@@ -961,6 +961,7 @@ When converting Figma to code, ensure:
|
|
|
961
961
|
- [ ] Added proper loading states
|
|
962
962
|
- [ ] Added proper error states
|
|
963
963
|
- [ ] Tested on multiple breakpoints (mobile, tablet, desktop)
|
|
964
|
+
- [ ] Implemented responsive navigation menu
|
|
964
965
|
|
|
965
966
|
---
|
|
966
967
|
|
|
@@ -968,51 +969,72 @@ When converting Figma to code, ensure:
|
|
|
968
969
|
|
|
969
970
|
Before finalizing code, verify:
|
|
970
971
|
|
|
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
|
+
|
|
971
977
|
### Icons & Assets
|
|
972
978
|
1. **Icons workflow** - All icons created via `pnpm generate-icons` (not manual)
|
|
973
|
-
2. **
|
|
974
|
-
3. **
|
|
975
|
-
4. **
|
|
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)
|
|
976
992
|
|
|
977
993
|
### Components & Styling
|
|
978
|
-
1. **HeroUI components first** - Verify HeroUI library was checked before custom
|
|
994
|
+
1. **HeroUI components first** - Verify HeroUI library was checked before custom
|
|
979
995
|
2. **No direct HeroUI imports** - Only through `@/components/ui`
|
|
980
|
-
3. **Component
|
|
981
|
-
4. **
|
|
982
|
-
5. **
|
|
983
|
-
6. **Standard Tailwind classes** - No arbitrary values when standard class exists
|
|
984
|
-
7. **Map from constants** - All menu items, dropdown options mapped from config
|
|
985
|
-
|
|
986
|
-
###
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
### TypeScript & Validation
|
|
998
|
-
12. **Proper TypeScript** - No `any`, all props typed
|
|
999
|
-
13. **Form validation** - Zod schemas for all forms
|
|
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
|
|
1000
1013
|
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
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
|
|
1004
1018
|
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
- **Styles reference**: `src/styles/globals.css`
|
|
1010
|
-
- **Theme config**: `src/styles/hero.ts`
|
|
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
|
|
1011
1023
|
|
|
1012
1024
|
---
|
|
1013
1025
|
|
|
1014
1026
|
## 🚀 Quick Reference Guide
|
|
1015
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
|
+
|
|
1016
1038
|
### Icons Workflow
|
|
1017
1039
|
```bash
|
|
1018
1040
|
# 1. Export SVG from Figma
|
|
@@ -1022,19 +1044,30 @@ mv icon.svg src/assets/icons/
|
|
|
1022
1044
|
# 3. Generate components
|
|
1023
1045
|
pnpm generate-icons
|
|
1024
1046
|
|
|
1025
|
-
# 4. Use
|
|
1026
|
-
import {
|
|
1027
|
-
<
|
|
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" />
|
|
1028
1054
|
```
|
|
1029
1055
|
|
|
1030
1056
|
### Logos/Images Workflow
|
|
1031
1057
|
```bash
|
|
1032
|
-
# 1.
|
|
1033
|
-
#
|
|
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
|
|
1034
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
|
|
1035
1070
|
|
|
1036
|
-
# 3. Create centralized constants file
|
|
1037
|
-
# src/config/images.ts
|
|
1038
1071
|
const LOGOS = {
|
|
1039
1072
|
PRIMARY: '/images/logos/logo-primary.svg',
|
|
1040
1073
|
ICON: '/images/logos/logo-icon.svg',
|
|
@@ -1049,11 +1082,60 @@ export const IMAGES = {
|
|
|
1049
1082
|
WEBSITE,
|
|
1050
1083
|
} as const;
|
|
1051
1084
|
|
|
1052
|
-
#
|
|
1085
|
+
# 5. Use in code
|
|
1053
1086
|
import { IMAGES } from '@/config/images';
|
|
1054
1087
|
<img src={IMAGES.LOGOS.PRIMARY} alt="Logo" className="h-10 w-auto" />
|
|
1055
|
-
|
|
1056
|
-
|
|
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>
|
|
1057
1139
|
```
|
|
1058
1140
|
|
|
1059
1141
|
### Tailwind Standard Classes
|
|
@@ -1072,48 +1154,32 @@ text-base = 16px text-2xl = 24px
|
|
|
1072
1154
|
text-lg = 18px text-3xl = 30px
|
|
1073
1155
|
|
|
1074
1156
|
// Use standard class if within 2-4px of target
|
|
1075
|
-
// Only use arbitrary [value] for design tokens
|
|
1076
1157
|
```
|
|
1077
1158
|
|
|
1078
|
-
###
|
|
1159
|
+
### Primary Colors Usage
|
|
1079
1160
|
```tsx
|
|
1080
|
-
//
|
|
1081
|
-
|
|
1082
|
-
<
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
import { NAVIGATION_URLS } from '@/config/navigationUrls';
|
|
1091
|
-
<Link href={NAVIGATION_URLS.ABOUT}>{t('about')}</Link>
|
|
1092
|
-
|
|
1093
|
-
// Navigation hooks (from next/navigation)
|
|
1094
|
-
import { usePathname, useRouter } from 'next/navigation';
|
|
1095
|
-
const pathname = usePathname();
|
|
1096
|
-
const router = useRouter();
|
|
1097
|
-
|
|
1098
|
-
// Translation (all text)
|
|
1099
|
-
const t = useTranslations('namespace');
|
|
1100
|
-
<h1>{t('title')}</h1>
|
|
1101
|
-
|
|
1102
|
-
// Components (use HeroUI, not HTML)
|
|
1103
|
-
import { Button } from '@/components/ui';
|
|
1104
|
-
<Button color="primary">Click</Button> // ✅
|
|
1105
|
-
<button>Click</button> // ❌
|
|
1106
|
-
|
|
1107
|
-
// Data mapping (from constants)
|
|
1108
|
-
const menuItems = [
|
|
1109
|
-
{ key: 'home', labelKey: 'home', href: NAVIGATION_URLS.ROOT },
|
|
1110
|
-
];
|
|
1111
|
-
{menuItems.map(item => <Item key={item.key}>{t(item.labelKey)}</Item>)}
|
|
1112
|
-
|
|
1113
|
-
// Responsive (mobile-first)
|
|
1114
|
-
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
|
|
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">
|
|
1115
1171
|
```
|
|
1116
1172
|
|
|
1117
1173
|
---
|
|
1118
1174
|
|
|
1119
|
-
**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.
|