@adlas/create-app 1.0.49 → 1.0.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/templates/docs/FIGMA_TO_CODE_GUIDE.md +1026 -660
|
@@ -4,86 +4,53 @@
|
|
|
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:
|
|
7
|
+
## 🔗 Working with Figma MCP Server
|
|
14
8
|
|
|
15
|
-
|
|
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
|
|
9
|
+
### CRITICAL: Use Figma Link, NOT Screenshots
|
|
19
10
|
|
|
20
|
-
|
|
11
|
+
**❌ NEVER use screenshots** - Screenshots lose design data, measurements, and context
|
|
21
12
|
|
|
22
|
-
|
|
13
|
+
**✅ ALWAYS use Figma MCP server with node links**:
|
|
23
14
|
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
|
|
15
|
+
```typescript
|
|
16
|
+
// Use get_design_context tool with node-id from Figma URL
|
|
17
|
+
// Example URL: https://figma.com/design/:fileKey/:fileName?node-id=8486-1580
|
|
18
|
+
// Extract node-id: 8486-1580 → use as: "8486:1580"
|
|
19
|
+
|
|
20
|
+
mcp__figma -
|
|
21
|
+
dev -
|
|
22
|
+
mode -
|
|
23
|
+
mcp -
|
|
24
|
+
server__get_design_context({
|
|
25
|
+
nodeId: "8486:1580",
|
|
26
|
+
clientLanguages: "typescript",
|
|
27
|
+
clientFrameworks: "react,nextjs",
|
|
28
|
+
});
|
|
27
29
|
```
|
|
28
30
|
|
|
29
|
-
|
|
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
|
|
31
|
+
**Benefits of using Figma MCP**:
|
|
35
32
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
```
|
|
33
|
+
- ✅ Get exact measurements, colors, and spacing
|
|
34
|
+
- ✅ Access design tokens and styles
|
|
35
|
+
- ✅ See component structure and hierarchy
|
|
36
|
+
- ✅ Get accurate typography settings
|
|
37
|
+
- ✅ Extract assets and icons properly
|
|
70
38
|
|
|
71
|
-
**
|
|
72
|
-
|
|
73
|
-
---
|
|
39
|
+
**Do NOT call `get_screenshot`** - The design context provides all needed information
|
|
74
40
|
|
|
75
41
|
## 🎯 Core Principles
|
|
76
42
|
|
|
77
|
-
1. **Use
|
|
78
|
-
2. **Use
|
|
79
|
-
3. **
|
|
80
|
-
4. **
|
|
81
|
-
5. **
|
|
82
|
-
6. **
|
|
83
|
-
7. **
|
|
84
|
-
8. **
|
|
85
|
-
9. **
|
|
86
|
-
10. **
|
|
43
|
+
1. **Use Figma MCP Server** - ALWAYS use `get_design_context` with Figma node links, NEVER screenshots
|
|
44
|
+
2. **Use HeroUI components first** - Always prefer HeroUI components. Only use Tailwind CSS for custom styling when HeroUI doesn't provide the component
|
|
45
|
+
3. **Use ONLY existing components** - Never create custom implementations when project components exist
|
|
46
|
+
4. **i18n for ALL text** - ALL labels, placeholders, titles, error messages MUST use translation keys
|
|
47
|
+
5. **Navigation from config** - NEVER hardcode URLs. Always use `NAVIGATION_URLS` from config
|
|
48
|
+
6. **Icons workflow** - Export SVG from Figma → Place in `src/assets/icons/` → Run `pnpm generate-icons` → Use generated TSX components
|
|
49
|
+
7. **Logos workflow** - Export SVG/PNG from Figma → Place in `public/` → Reference via path (prefer SVG over PNG)
|
|
50
|
+
8. **Use Tailwind standard classes** - Use closest Tailwind class instead of custom values (e.g., `h-10` not `h-[41px]`)
|
|
51
|
+
9. **Follow project structure** - Place files in correct locations as defined below
|
|
52
|
+
10. **Match coding patterns** - Use the same patterns found in existing code
|
|
53
|
+
11. **Type safety first** - Use TypeScript strictly, no `any` types
|
|
87
54
|
|
|
88
55
|
---
|
|
89
56
|
|
|
@@ -91,27 +58,35 @@ When you receive a Figma URL:
|
|
|
91
58
|
|
|
92
59
|
### CRITICAL: Component Styles Must Be Configured, Not Inline
|
|
93
60
|
|
|
94
|
-
**❌ WRONG - Inline
|
|
61
|
+
**❌ WRONG - Inline component styles**:
|
|
62
|
+
|
|
95
63
|
```tsx
|
|
96
|
-
//
|
|
64
|
+
// DON'T: Apply component-specific styles inline via className
|
|
97
65
|
<Button className="h-10 rounded-full bg-[#19ffa3] px-4 text-sm text-black">
|
|
98
66
|
Click Me
|
|
99
67
|
</Button>
|
|
100
68
|
|
|
101
|
-
//
|
|
69
|
+
// DON'T: Mix props and className for same property
|
|
70
|
+
<Button size="md" className="h-10 px-4"> {/* redundant */}
|
|
71
|
+
Click Me
|
|
72
|
+
</Button>
|
|
73
|
+
|
|
74
|
+
// DON'T: Use HTML elements when HeroUI component exists
|
|
102
75
|
<button onClick={handler}>Click</button>
|
|
103
76
|
```
|
|
104
77
|
|
|
105
|
-
**✅ CORRECT -
|
|
78
|
+
**✅ CORRECT - Use props and configured variants**:
|
|
79
|
+
|
|
106
80
|
```tsx
|
|
81
|
+
// Step 1: Configure variants in component wrapper
|
|
107
82
|
// File: src/components/ui/Button.tsx
|
|
108
83
|
import { extendVariants, Button as HeroUIButton } from '@heroui/react';
|
|
109
84
|
|
|
110
85
|
export const Button = extendVariants(HeroUIButton, {
|
|
111
86
|
variants: {
|
|
112
87
|
color: {
|
|
113
|
-
|
|
114
|
-
|
|
88
|
+
// ✅ Use theme colors from hero.ts, NOT custom colors
|
|
89
|
+
primary: 'bg-primary text-black',
|
|
115
90
|
},
|
|
116
91
|
size: {
|
|
117
92
|
sm: 'h-8 px-3 text-xs',
|
|
@@ -120,58 +95,112 @@ export const Button = extendVariants(HeroUIButton, {
|
|
|
120
95
|
},
|
|
121
96
|
},
|
|
122
97
|
defaultVariants: {
|
|
123
|
-
color: 'primary',
|
|
124
98
|
size: 'md',
|
|
125
|
-
radius: 'full',
|
|
126
99
|
},
|
|
127
100
|
});
|
|
128
101
|
|
|
129
|
-
//
|
|
130
|
-
|
|
131
|
-
|
|
102
|
+
// Step 2: Use ONLY props in components (NO className for component styles)
|
|
103
|
+
import { Button } from '@/components/ui';
|
|
104
|
+
|
|
105
|
+
<Button color="primary"> {/* size="md" not needed - it's the default */}
|
|
106
|
+
Click Me
|
|
107
|
+
</Button>
|
|
108
|
+
|
|
109
|
+
// ✅ className ONLY for layout utilities
|
|
110
|
+
<Button
|
|
111
|
+
color="primary" {/* Only specify non-default variants */}
|
|
112
|
+
className="mt-4 hidden lg:block" // Layout utilities only
|
|
113
|
+
>
|
|
114
|
+
Click Me
|
|
115
|
+
</Button>
|
|
116
|
+
|
|
117
|
+
// ✅ Override default when needed
|
|
118
|
+
<Button color="primary" size="lg"> {/* Explicitly override default size */}
|
|
119
|
+
Large Button
|
|
120
|
+
</Button>
|
|
132
121
|
```
|
|
133
122
|
|
|
134
|
-
**
|
|
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)
|
|
123
|
+
**Component Style Rules**:
|
|
138
124
|
|
|
139
|
-
|
|
125
|
+
- ✅ **Configure in wrapper**: All component-specific styles (height, padding, colors, text size)
|
|
126
|
+
- ✅ **Set defaultVariants**: Define defaults in wrapper so you don't repeat them in usage
|
|
127
|
+
- ✅ **Use theme colors**: `bg-primary`, `bg-secondary`, `bg-success`, `bg-danger`, `bg-warning` from hero.ts
|
|
128
|
+
- ✅ **Use props**: Only specify props that differ from defaults (e.g., `color="primary"`, `size="lg"`)
|
|
129
|
+
- ✅ **Use HeroUI props for styling**: HeroUI components have built-in props like `radius`, `isRounded`, etc.
|
|
130
|
+
- ✅ **className for layout ONLY**: `mt-4`, `hidden`, `lg:block`, `justify-start`, `text-white`
|
|
131
|
+
- ❌ **NEVER use custom colors**: `bg-[#19ffa3]`, `text-[#ff0000]` - Use theme colors instead
|
|
132
|
+
- ❌ **NEVER use hover states in config**: `hover:bg-[#00e68a]` - HeroUI handles hover states
|
|
133
|
+
- ❌ **NEVER className for**: `h-10`, `px-4`, `text-sm`, `bg-[color]`, `rounded-full`
|
|
134
|
+
- ❌ **NEVER repeat defaultVariants**: Don't specify `size="md"` if `md` is already the default
|
|
140
135
|
|
|
141
|
-
|
|
136
|
+
**Color Usage Rules**:
|
|
142
137
|
|
|
143
138
|
```tsx
|
|
144
|
-
|
|
139
|
+
// ✅ CORRECT - Use theme colors from hero.ts or globals.css
|
|
140
|
+
color: {
|
|
141
|
+
primary: 'bg-primary text-black',
|
|
142
|
+
secondary: 'bg-secondary text-white',
|
|
143
|
+
success: 'bg-success text-white',
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ❌ WRONG - Custom color values or hover states
|
|
147
|
+
color: {
|
|
148
|
+
primary: 'bg-[#19ffa3] text-black hover:bg-[#00e68a]', // Don't use hex or hover
|
|
149
|
+
custom: 'bg-[rgb(25,255,163)]', // Don't use rgb
|
|
150
|
+
}
|
|
145
151
|
|
|
146
|
-
|
|
152
|
+
// Available theme colors (from hero.ts):
|
|
153
|
+
primary-50, primary-100, ..., primary-500, ..., primary-900
|
|
154
|
+
// And HeroUI defaults:
|
|
155
|
+
secondary, success, danger, warning, default, foreground, background
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**What goes where**:
|
|
159
|
+
|
|
160
|
+
```tsx
|
|
161
|
+
// In Button.tsx configuration:
|
|
162
|
+
const Button = extendVariants(HerouiButton, {
|
|
147
163
|
variants: {
|
|
148
|
-
variant: {
|
|
149
|
-
flat: 'bg-gray-100 border-none',
|
|
150
|
-
bordered: 'border-2 border-gray-300',
|
|
151
|
-
},
|
|
152
164
|
size: {
|
|
153
|
-
sm: 'h-8 text-xs',
|
|
154
|
-
md: 'h-10 text-sm',
|
|
155
|
-
lg: 'h-12 text-base',
|
|
165
|
+
sm: 'h-8 px-3 text-xs',
|
|
166
|
+
md: 'h-10 px-4 text-sm', // Height, padding, text size
|
|
167
|
+
lg: 'h-12 px-6 text-base',
|
|
168
|
+
},
|
|
169
|
+
color: {
|
|
170
|
+
primary: 'bg-primary text-black', // Theme colors ONLY, NO hover states
|
|
156
171
|
},
|
|
157
172
|
},
|
|
158
173
|
defaultVariants: {
|
|
159
|
-
|
|
160
|
-
size: 'md',
|
|
161
|
-
radius: 'lg',
|
|
174
|
+
size: 'md', // Default size - no need to specify in usage
|
|
162
175
|
},
|
|
163
176
|
});
|
|
164
|
-
```
|
|
165
177
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
-
|
|
170
|
-
-
|
|
178
|
+
// In component usage:
|
|
179
|
+
<Button
|
|
180
|
+
color="primary" // ✅ Specify only when different from default
|
|
181
|
+
radius="full" // ✅ HeroUI built-in prop (don't configure in wrapper)
|
|
182
|
+
className="mt-4 hidden lg:block" // ✅ Layout utilities only
|
|
183
|
+
>
|
|
184
|
+
{t('navbar.bookDemo')} // ✅ i18n text
|
|
185
|
+
</Button>
|
|
186
|
+
// Note: size="md" NOT needed because it's the defaultVariant
|
|
187
|
+
|
|
188
|
+
// Using size prop when you need different size:
|
|
189
|
+
<Button color="primary" size="lg"> // ✅ Override default size
|
|
190
|
+
Large Button
|
|
191
|
+
</Button>
|
|
192
|
+
|
|
193
|
+
// Using Icon component:
|
|
194
|
+
import { Icon } from '@/components/ui';
|
|
195
|
+
|
|
196
|
+
<Icon name="ChevronDownIcon" className="size-4" /> // ✅ CORRECT - PascalCase name
|
|
197
|
+
<ChevronDownIcon className="size-4" /> // ❌ WRONG - Don't import individual icons
|
|
198
|
+
```
|
|
171
199
|
|
|
172
200
|
### Data Must Be Mapped from Constants
|
|
173
201
|
|
|
174
202
|
**❌ WRONG - Hardcoded data**:
|
|
203
|
+
|
|
175
204
|
```tsx
|
|
176
205
|
<DropdownMenu>
|
|
177
206
|
<DropdownItem key="option1">Option 1</DropdownItem>
|
|
@@ -181,19 +210,17 @@ export const Input = extendVariants(HeroUIInput, {
|
|
|
181
210
|
```
|
|
182
211
|
|
|
183
212
|
**✅ CORRECT - Map from constants**:
|
|
213
|
+
|
|
184
214
|
```tsx
|
|
185
215
|
// File: src/config/menuOptions.ts
|
|
186
216
|
export const productMenuItems = [
|
|
187
|
-
{ key:
|
|
188
|
-
{ key:
|
|
189
|
-
{ key:
|
|
190
|
-
]
|
|
217
|
+
{ key: "analytics", labelKey: "product.analytics", href: "/analytics" },
|
|
218
|
+
{ key: "insights", labelKey: "product.insights", href: "/insights" },
|
|
219
|
+
{ key: "reports", labelKey: "product.reports", href: "/reports" },
|
|
220
|
+
];
|
|
191
221
|
|
|
192
222
|
// Usage:
|
|
193
|
-
import { productMenuItems } from
|
|
194
|
-
import { useTranslations } from 'next-intl';
|
|
195
|
-
|
|
196
|
-
const t = useTranslations();
|
|
223
|
+
import { productMenuItems } from "@/config/menuOptions";
|
|
197
224
|
|
|
198
225
|
<DropdownMenu>
|
|
199
226
|
{productMenuItems.map((item) => (
|
|
@@ -201,254 +228,336 @@ const t = useTranslations();
|
|
|
201
228
|
{t(item.labelKey)}
|
|
202
229
|
</DropdownItem>
|
|
203
230
|
))}
|
|
204
|
-
</DropdownMenu
|
|
231
|
+
</DropdownMenu>;
|
|
205
232
|
```
|
|
206
233
|
|
|
207
234
|
---
|
|
208
235
|
|
|
209
|
-
## 🎨
|
|
236
|
+
## 🎨 Component Priority & Usage Strategy
|
|
210
237
|
|
|
211
|
-
###
|
|
238
|
+
### 1. Check HeroUI First
|
|
212
239
|
|
|
213
|
-
|
|
240
|
+
Before creating ANY component, check if HeroUI provides it:
|
|
214
241
|
|
|
215
|
-
|
|
216
|
-
|
|
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
|
-
```
|
|
242
|
+
- **HeroUI Documentation**: https://heroui.com/docs/components
|
|
243
|
+
- **HeroUI Figma Kit**: https://www.figma.com/design/kFGcjHsNKZx7zh2NxEJXYt/HeroUI-Figma-Kit--Community---Community-
|
|
230
244
|
|
|
231
|
-
###
|
|
245
|
+
### 2. Component Selection Flow
|
|
232
246
|
|
|
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 */}
|
|
250
247
|
```
|
|
248
|
+
1. Does HeroUI have this component?
|
|
249
|
+
→ YES: Use HeroUI component (via @/components/ui wrapper if exists)
|
|
250
|
+
→ NO: Go to step 2
|
|
251
251
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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
|
-
---
|
|
252
|
+
2. Does project have wrapper in src/components/ui/?
|
|
253
|
+
→ YES: Use the wrapper
|
|
254
|
+
→ NO: Go to step 3
|
|
263
255
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
256
|
+
3. Build with Tailwind CSS utilities
|
|
257
|
+
→ Use HeroUI theme colors and patterns
|
|
258
|
+
→ Follow mobile-first responsive design
|
|
259
|
+
```
|
|
267
260
|
|
|
268
|
-
|
|
261
|
+
### 3. Example Decision Tree
|
|
269
262
|
|
|
270
263
|
```tsx
|
|
271
|
-
|
|
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';
|
|
264
|
+
// Need a Button?
|
|
265
|
+
import { Button } from '@/components/ui'; // ✅ Use HeroUI Button wrapper
|
|
278
266
|
|
|
279
|
-
//
|
|
280
|
-
//
|
|
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;
|
|
267
|
+
// Need an Input?
|
|
268
|
+
import { Input } from '@/components/ui'; // ✅ Use HeroUI Input wrapper
|
|
287
269
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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
|
-
}
|
|
270
|
+
// Need a Card?
|
|
271
|
+
// HeroUI has Card, but check if wrapper exists in project
|
|
272
|
+
// If not, use Tailwind utilities with HeroUI colors:
|
|
273
|
+
<div className="rounded-lg bg-white p-6 shadow-md"> // ✅ Custom with Tailwind
|
|
331
274
|
```
|
|
332
275
|
|
|
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
|
-
|
|
341
276
|
---
|
|
342
277
|
|
|
343
278
|
## 📁 Project Structure
|
|
344
279
|
|
|
345
280
|
### Pages Location
|
|
281
|
+
|
|
346
282
|
- **With i18n**: `src/app/[locale]/(route-group)/page.tsx`
|
|
347
283
|
- **Without i18n**: `src/app/(route-group)/page.tsx`
|
|
348
284
|
|
|
349
285
|
### Components Location
|
|
286
|
+
|
|
350
287
|
```
|
|
351
288
|
src/components/
|
|
352
|
-
├── ui/ # Reusable UI components (HeroUI wrappers
|
|
353
|
-
├── icons/ #
|
|
289
|
+
├── ui/ # Reusable UI components (HeroUI wrappers)
|
|
290
|
+
├── icons/ # Icon components
|
|
354
291
|
├── layout/ # Layout components (Navbar, Footer)
|
|
355
292
|
└── [feature]/ # Feature-specific components
|
|
356
293
|
```
|
|
357
294
|
|
|
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
|
-
|
|
367
295
|
### Styling
|
|
296
|
+
|
|
368
297
|
- **Global styles**: `src/styles/globals.css`
|
|
369
|
-
- **Theme config**: `src/styles/hero.ts`
|
|
298
|
+
- **Theme config**: `src/styles/hero.ts`
|
|
370
299
|
- **Approach**: Tailwind CSS utility classes ONLY
|
|
371
300
|
- **NO CSS modules, NO styled-components, NO inline styles**
|
|
372
301
|
|
|
373
302
|
---
|
|
374
303
|
|
|
375
|
-
##
|
|
304
|
+
## 🎨 Available UI Components
|
|
305
|
+
|
|
306
|
+
### Navigation Components
|
|
376
307
|
|
|
377
|
-
|
|
308
|
+
#### Navbar (HeroUI)
|
|
378
309
|
|
|
379
|
-
**
|
|
310
|
+
**CRITICAL**: Always use HeroUI Navbar component for navigation bars:
|
|
380
311
|
|
|
381
312
|
```tsx
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
313
|
+
import {
|
|
314
|
+
Navbar,
|
|
315
|
+
NavbarBrand,
|
|
316
|
+
NavbarContent,
|
|
317
|
+
NavbarItem,
|
|
318
|
+
NavbarMenu,
|
|
319
|
+
NavbarMenuItem,
|
|
320
|
+
NavbarMenuToggle,
|
|
321
|
+
} from "@heroui/react";
|
|
322
|
+
import { Button } from "@/components/ui";
|
|
323
|
+
|
|
324
|
+
<Navbar maxWidth="full" className="bg-black border-b-2">
|
|
325
|
+
{/* Logo */}
|
|
326
|
+
<NavbarBrand as={Link} href="/">
|
|
327
|
+
<img src="/logo.svg" alt="Logo" />
|
|
328
|
+
</NavbarBrand>
|
|
385
329
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
330
|
+
{/* Desktop Menu */}
|
|
331
|
+
<NavbarContent className="hidden lg:flex" justify="center">
|
|
332
|
+
<NavbarItem>
|
|
333
|
+
<Button variant="light">Menu Item</Button>{" "}
|
|
334
|
+
{/* size="md" omitted - it's default */}
|
|
335
|
+
</NavbarItem>
|
|
336
|
+
</NavbarContent>
|
|
389
337
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
338
|
+
{/* CTA */}
|
|
339
|
+
<NavbarContent className="hidden lg:flex" justify="end">
|
|
340
|
+
<NavbarItem>
|
|
341
|
+
<Button color="primary">CTA</Button>{" "}
|
|
342
|
+
{/* size="md" omitted - it's default */}
|
|
343
|
+
</NavbarItem>
|
|
344
|
+
</NavbarContent>
|
|
394
345
|
|
|
395
|
-
|
|
396
|
-
|
|
346
|
+
{/* Mobile Toggle */}
|
|
347
|
+
<NavbarMenuToggle className="lg:hidden" />
|
|
348
|
+
|
|
349
|
+
{/* Mobile Menu */}
|
|
350
|
+
<NavbarMenu>
|
|
351
|
+
<NavbarMenuItem>
|
|
352
|
+
<Button variant="light" fullWidth>
|
|
353
|
+
Menu Item
|
|
354
|
+
</Button>
|
|
355
|
+
</NavbarMenuItem>
|
|
356
|
+
</NavbarMenu>
|
|
357
|
+
</Navbar>;
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
**Key Points**:
|
|
361
|
+
|
|
362
|
+
- ✅ Use `Navbar` from `@heroui/react`
|
|
363
|
+
- ✅ Use `Button` from `@/components/ui` for menu items
|
|
364
|
+
- ✅ Use props (`color`, `size`, `variant`) NOT className for styles
|
|
365
|
+
- ✅ className ONLY for layout (`hidden`, `lg:flex`, `bg-black`)
|
|
366
|
+
- ❌ DON'T use custom `<header>` or `<nav>` tags
|
|
367
|
+
|
|
368
|
+
### Form Components
|
|
369
|
+
|
|
370
|
+
All located in `src/components/ui/`:
|
|
371
|
+
|
|
372
|
+
#### Input Fields
|
|
373
|
+
|
|
374
|
+
```tsx
|
|
375
|
+
import { Input, PasswordInput, NumberInput, Textarea } from '@/components/ui';
|
|
376
|
+
|
|
377
|
+
// Text Input
|
|
378
|
+
<Input
|
|
379
|
+
label="Email"
|
|
380
|
+
placeholder="Enter your email"
|
|
381
|
+
type="email"
|
|
382
|
+
isRequired
|
|
383
|
+
/>
|
|
384
|
+
|
|
385
|
+
// Password Input (with show/hide toggle)
|
|
386
|
+
<PasswordInput
|
|
387
|
+
label="Password"
|
|
388
|
+
placeholder="Enter password"
|
|
389
|
+
isRequired
|
|
390
|
+
/>
|
|
391
|
+
|
|
392
|
+
// Number Input (with increment/decrement)
|
|
393
|
+
<NumberInput
|
|
394
|
+
label="Quantity"
|
|
395
|
+
min={1}
|
|
396
|
+
max={100}
|
|
397
|
+
defaultValue={1}
|
|
398
|
+
/>
|
|
399
|
+
|
|
400
|
+
// Textarea
|
|
401
|
+
<Textarea
|
|
402
|
+
label="Description"
|
|
403
|
+
placeholder="Enter description"
|
|
404
|
+
rows={4}
|
|
405
|
+
/>
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
#### Selection Components
|
|
409
|
+
|
|
410
|
+
```tsx
|
|
411
|
+
import { Select, RadioGroup, Checkbox, Autocomplete } from '@/components/ui';
|
|
412
|
+
|
|
413
|
+
// Select Dropdown
|
|
414
|
+
<Select
|
|
415
|
+
label="Country"
|
|
416
|
+
placeholder="Select country"
|
|
417
|
+
items={countries}
|
|
418
|
+
/>
|
|
419
|
+
|
|
420
|
+
// Radio Group
|
|
421
|
+
<RadioGroup
|
|
422
|
+
label="Size"
|
|
423
|
+
options={[
|
|
424
|
+
{ value: 's', label: 'Small' },
|
|
425
|
+
{ value: 'm', label: 'Medium' },
|
|
426
|
+
{ value: 'l', label: 'Large' },
|
|
427
|
+
]}
|
|
428
|
+
/>
|
|
429
|
+
|
|
430
|
+
// Checkbox
|
|
431
|
+
<Checkbox>I agree to terms</Checkbox>
|
|
432
|
+
|
|
433
|
+
// Autocomplete (with search)
|
|
434
|
+
<Autocomplete
|
|
435
|
+
label="Search"
|
|
436
|
+
items={suggestions}
|
|
437
|
+
placeholder="Type to search..."
|
|
438
|
+
/>
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
#### Date & Time
|
|
442
|
+
|
|
443
|
+
```tsx
|
|
444
|
+
import { DatePicker } from "@/components/ui";
|
|
445
|
+
|
|
446
|
+
<DatePicker
|
|
447
|
+
label="Select Date"
|
|
448
|
+
placeholderValue={new CalendarDate(2024, 1, 1)}
|
|
449
|
+
/>;
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
#### Buttons
|
|
453
|
+
|
|
454
|
+
```tsx
|
|
455
|
+
import { Button } from '@/components/ui';
|
|
456
|
+
|
|
457
|
+
<Button color="primary"> {/* size="md" omitted - it's default */}
|
|
458
|
+
Submit
|
|
459
|
+
</Button>
|
|
397
460
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
461
|
+
<Button color="secondary" variant="bordered">
|
|
462
|
+
Cancel
|
|
463
|
+
</Button>
|
|
464
|
+
|
|
465
|
+
<Button isLoading>
|
|
466
|
+
Processing...
|
|
467
|
+
</Button>
|
|
402
468
|
```
|
|
403
469
|
|
|
404
|
-
|
|
405
|
-
- No need to import individual icons
|
|
406
|
-
- Cleaner component code
|
|
407
|
-
- Easy to swap icons by changing name prop
|
|
408
|
-
- Consistent icon rendering
|
|
470
|
+
#### Other Components
|
|
409
471
|
|
|
410
|
-
|
|
472
|
+
```tsx
|
|
473
|
+
import { Modal, Chip, Breadcrumbs, Tabs, Icon } from '@/components/ui';
|
|
411
474
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
475
|
+
// Modal
|
|
476
|
+
<Modal
|
|
477
|
+
isOpen={isOpen}
|
|
478
|
+
onClose={onClose}
|
|
479
|
+
title="Modal Title"
|
|
480
|
+
>
|
|
481
|
+
Modal content
|
|
482
|
+
</Modal>
|
|
483
|
+
|
|
484
|
+
// Chip (badges/tags)
|
|
485
|
+
<Chip color="success" variant="flat">
|
|
486
|
+
Active
|
|
487
|
+
</Chip>
|
|
488
|
+
|
|
489
|
+
// Breadcrumbs
|
|
490
|
+
<Breadcrumbs
|
|
491
|
+
items={[
|
|
492
|
+
{ label: 'Home', href: '/' },
|
|
493
|
+
{ label: 'Products', href: '/products' },
|
|
494
|
+
]}
|
|
495
|
+
/>
|
|
416
496
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
497
|
+
// Tabs
|
|
498
|
+
<Tabs
|
|
499
|
+
items={[
|
|
500
|
+
{ key: 'tab1', label: 'Tab 1', content: <div>Content 1</div> },
|
|
501
|
+
{ key: 'tab2', label: 'Tab 2', content: <div>Content 2</div> },
|
|
502
|
+
]}
|
|
503
|
+
/>
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### Icons
|
|
421
507
|
|
|
422
|
-
|
|
423
|
-
```bash
|
|
424
|
-
pnpm generate-icons
|
|
425
|
-
```
|
|
508
|
+
**CRITICAL WORKFLOW**: Icons must follow this exact process:
|
|
426
509
|
|
|
427
|
-
|
|
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
|
|
510
|
+
#### Step 1: Export SVG from Figma
|
|
432
511
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
512
|
+
1. Select the icon in Figma design
|
|
513
|
+
2. Export as SVG (not PNG/JPG)
|
|
514
|
+
3. Download the SVG file
|
|
436
515
|
|
|
437
|
-
|
|
438
|
-
```
|
|
516
|
+
#### Step 2: Place in Icons Folder
|
|
439
517
|
|
|
440
|
-
|
|
518
|
+
```bash
|
|
519
|
+
# Place SVG file in src/assets/icons/
|
|
520
|
+
mv downloaded-icon.svg src/assets/icons/icon-name.svg
|
|
521
|
+
```
|
|
441
522
|
|
|
442
|
-
|
|
523
|
+
#### Step 3: Generate React Components
|
|
524
|
+
|
|
525
|
+
```bash
|
|
526
|
+
# Run the icon generation script
|
|
527
|
+
pnpm generate-icons
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
This command will:
|
|
531
|
+
|
|
532
|
+
- Convert all SVG files in `src/assets/icons/` to React components
|
|
533
|
+
- Place them in `src/components/icons/`
|
|
534
|
+
- Auto-format and lint the generated code
|
|
535
|
+
- Update `src/components/icons/index.ts` with exports
|
|
536
|
+
|
|
537
|
+
#### Step 4: Use in Code
|
|
538
|
+
|
|
539
|
+
**CORRECT USAGE**: Use the `Icon` component with `name` prop (PascalCase):
|
|
443
540
|
|
|
444
541
|
```tsx
|
|
445
|
-
import {
|
|
542
|
+
import { Icon } from '@/components/ui';
|
|
446
543
|
|
|
447
|
-
|
|
448
|
-
<
|
|
544
|
+
// ✅ CORRECT - Using Icon component with PascalCase name
|
|
545
|
+
<Icon name="ChevronDownIcon" className="size-5 text-white" />
|
|
546
|
+
<Icon name="CloseIcon" className="size-4" />
|
|
547
|
+
<Icon name="EyeIcon" className="size-6" />
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
**❌ WRONG USAGE - DO NOT import individual icon components**:
|
|
551
|
+
|
|
552
|
+
```tsx
|
|
553
|
+
// ❌ WRONG - Don't import and use individual icon components
|
|
554
|
+
import { ChevronDownIcon, CloseIcon } from '@/components/icons';
|
|
555
|
+
<ChevronDownIcon className="size-5" />
|
|
556
|
+
<CloseIcon className="size-4" />
|
|
449
557
|
```
|
|
450
558
|
|
|
451
559
|
**Available Icons** (auto-generated):
|
|
560
|
+
|
|
452
561
|
- ChevronDownIcon, ChevronRightIcon
|
|
453
562
|
- CloseIcon, MenuIcon
|
|
454
563
|
- EyeIcon, EyeSlashIcon
|
|
@@ -457,115 +566,115 @@ import { ChevronDownIcon, MenuIcon } from '@/components/icons';
|
|
|
457
566
|
- DotsVerticalIcon, FrameIcon
|
|
458
567
|
- And more...
|
|
459
568
|
|
|
569
|
+
**Icon Names**:
|
|
570
|
+
|
|
571
|
+
- Icon names are PascalCase with "Icon" suffix (e.g., `ChevronDownIcon`)
|
|
572
|
+
- Generated from SVG filename: `chevron-down.svg` → component `ChevronDownIcon`
|
|
573
|
+
- The Icon component uses the exact component name from `@/components/icons`
|
|
574
|
+
|
|
460
575
|
**❌ DO NOT**:
|
|
576
|
+
|
|
577
|
+
- Import individual icon components directly
|
|
578
|
+
- Use kebab-case names (use `ChevronDownIcon` not `chevron-down`)
|
|
461
579
|
- Use Lucide, Font Awesome, or other icon libraries
|
|
462
580
|
- Create manual icon components
|
|
463
581
|
- Use `<img>` tags for icons
|
|
464
582
|
- Hardcode SVG paths directly in components
|
|
465
|
-
- Edit files in `src/components/icons/` manually (they are auto-generated)
|
|
466
583
|
|
|
467
584
|
**✅ DO**:
|
|
585
|
+
|
|
586
|
+
- Always use `<Icon name="IconName" />` pattern with PascalCase
|
|
468
587
|
- Always export from Figma as SVG
|
|
469
588
|
- Use `pnpm generate-icons` to create components
|
|
470
|
-
- Use `<Icon name="IconName" />` pattern for cleaner code
|
|
471
589
|
- Use Tailwind classes for sizing (`size-4`, `size-5`, `size-6`)
|
|
472
|
-
- Organize icons in `src/assets/icons/` with descriptive names
|
|
473
590
|
|
|
474
|
-
|
|
591
|
+
### Logos & Images
|
|
475
592
|
|
|
476
|
-
|
|
593
|
+
**CRITICAL WORKFLOW**: Logos and images must be placed in `public/` directory:
|
|
477
594
|
|
|
478
|
-
|
|
595
|
+
#### Step 1: Check Existing Images First
|
|
479
596
|
|
|
480
|
-
**
|
|
597
|
+
**BEFORE implementing or exporting from Figma**:
|
|
598
|
+
|
|
599
|
+
1. Check `public/images/index.ts` to see if the image/logo already exists
|
|
600
|
+
2. Check `public/images/logos/` directory for available logo files
|
|
601
|
+
3. Only export from Figma if the asset doesn't exist
|
|
602
|
+
|
|
603
|
+
```bash
|
|
604
|
+
# Check what images are available
|
|
605
|
+
cat public/images/index.ts
|
|
606
|
+
ls public/images/logos/
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
**Why this matters**:
|
|
610
|
+
|
|
611
|
+
- Avoid duplicate assets
|
|
612
|
+
- User may have already exported the assets
|
|
613
|
+
- Maintain consistency with existing naming conventions
|
|
614
|
+
|
|
615
|
+
#### Step 2: Export from Figma (if needed)
|
|
481
616
|
|
|
482
617
|
1. **For logos**: Export as SVG (preferred) or PNG if source is raster
|
|
483
618
|
2. **For images**: Export as PNG, JPG, or WebP based on image type
|
|
484
619
|
3. Use appropriate resolution (2x or 3x for retina displays)
|
|
485
620
|
|
|
486
|
-
|
|
621
|
+
#### Step 3: Place in Public Folder
|
|
487
622
|
|
|
488
623
|
```bash
|
|
624
|
+
# Place logo/image files in public/
|
|
489
625
|
public/
|
|
626
|
+
├── logo.svg # Main logo (SVG preferred)
|
|
627
|
+
├── logo-dark.svg # Dark mode logo
|
|
628
|
+
├── logo.png # Fallback PNG
|
|
629
|
+
├── favicon.ico
|
|
490
630
|
└── images/
|
|
491
|
-
├──
|
|
492
|
-
|
|
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
|
|
631
|
+
├── hero-bg.jpg
|
|
632
|
+
└── product-1.png
|
|
514
633
|
```
|
|
515
634
|
|
|
516
|
-
|
|
635
|
+
#### Step 4: Create Centralized Image Constants
|
|
517
636
|
|
|
518
|
-
**CRITICAL**:
|
|
637
|
+
**CRITICAL**: Create a centralized constants file for all image paths:
|
|
519
638
|
|
|
520
639
|
```typescript
|
|
521
640
|
// src/config/images.ts
|
|
522
641
|
|
|
523
642
|
/**
|
|
524
643
|
* 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" />
|
|
644
|
+
* Organize by category: LOGOS, AUTH, WEBSITE, SERVICES, etc.
|
|
536
645
|
*/
|
|
537
646
|
|
|
538
647
|
const LOGOS = {
|
|
539
|
-
PRIMARY_CIRCLE:
|
|
540
|
-
SECONDARY_CIRCLE:
|
|
541
|
-
FULL:
|
|
542
|
-
FULL_BLACK:
|
|
543
|
-
FULL_WHITE:
|
|
544
|
-
ICON:
|
|
648
|
+
PRIMARY_CIRCLE: "/images/logos/logo-circle-primary.svg",
|
|
649
|
+
SECONDARY_CIRCLE: "/images/logos/logo-circle-secondary.svg",
|
|
650
|
+
FULL: "/images/logos/logo-full.svg",
|
|
651
|
+
FULL_BLACK: "/images/logos/logo-full-black.svg",
|
|
652
|
+
FULL_WHITE: "/images/logos/logo-full-white.svg",
|
|
653
|
+
ICON: "/images/logos/logo-icon.svg",
|
|
545
654
|
} as const;
|
|
546
655
|
|
|
547
656
|
const AUTH = {
|
|
548
|
-
LOGIN_BG:
|
|
549
|
-
SIGNUP_BG:
|
|
550
|
-
RESET_PASSWORD:
|
|
657
|
+
LOGIN_BG: "/images/auth/login-background.jpg",
|
|
658
|
+
SIGNUP_BG: "/images/auth/signup-background.jpg",
|
|
659
|
+
RESET_PASSWORD: "/images/auth/reset-password.jpg",
|
|
551
660
|
} as const;
|
|
552
661
|
|
|
553
662
|
const WEBSITE = {
|
|
554
|
-
HOME_HERO:
|
|
555
|
-
HOME_NEWSLETTER:
|
|
556
|
-
ABOUT_HEADER:
|
|
557
|
-
CONTACT_MAP:
|
|
663
|
+
HOME_HERO: "/images/website/home-hero.webp",
|
|
664
|
+
HOME_NEWSLETTER: "/images/website/home-newsletter.webp",
|
|
665
|
+
ABOUT_HEADER: "/images/website/about-header.jpg",
|
|
666
|
+
CONTACT_MAP: "/images/website/contact-map.png",
|
|
558
667
|
} as const;
|
|
559
668
|
|
|
560
669
|
const SERVICES = {
|
|
561
|
-
HEADER:
|
|
562
|
-
FEATURE_1:
|
|
563
|
-
FEATURE_2:
|
|
670
|
+
HEADER: "/images/services/services-header.webp",
|
|
671
|
+
FEATURE_1: "/images/services/feature-1.jpg",
|
|
672
|
+
FEATURE_2: "/images/services/feature-2.jpg",
|
|
564
673
|
} as const;
|
|
565
674
|
|
|
566
675
|
const PRODUCTS = {
|
|
567
|
-
THUMBNAIL_DEFAULT:
|
|
568
|
-
HERO:
|
|
676
|
+
THUMBNAIL_DEFAULT: "/images/products/thumbnail-default.png",
|
|
677
|
+
HERO: "/images/products/hero-banner.jpg",
|
|
569
678
|
} as const;
|
|
570
679
|
|
|
571
680
|
export const IMAGES = {
|
|
@@ -577,13 +686,14 @@ export const IMAGES = {
|
|
|
577
686
|
} as const;
|
|
578
687
|
```
|
|
579
688
|
|
|
580
|
-
|
|
689
|
+
#### Step 5: Reference in Code
|
|
581
690
|
|
|
582
691
|
```tsx
|
|
692
|
+
// Import from centralized constants
|
|
583
693
|
import { IMAGES } from '@/config/images';
|
|
584
694
|
import Image from 'next/image';
|
|
585
695
|
|
|
586
|
-
//
|
|
696
|
+
// Using Next.js Image component (recommended)
|
|
587
697
|
<Image
|
|
588
698
|
src={IMAGES.LOGOS.FULL}
|
|
589
699
|
alt="Company Logo"
|
|
@@ -592,88 +702,157 @@ import Image from 'next/image';
|
|
|
592
702
|
priority
|
|
593
703
|
/>
|
|
594
704
|
|
|
595
|
-
//
|
|
705
|
+
// Using standard img tag (for SVGs with CSS control)
|
|
596
706
|
<img src={IMAGES.LOGOS.PRIMARY_CIRCLE} alt="Logo" className="h-10 w-auto" />
|
|
597
707
|
|
|
598
|
-
//
|
|
599
|
-
<div
|
|
600
|
-
className="bg-cover bg-center"
|
|
601
|
-
style={{ backgroundImage: `url(${IMAGES.WEBSITE.HOME_HERO})` }}
|
|
602
|
-
>
|
|
708
|
+
// Background images via Tailwind
|
|
709
|
+
<div className={`bg-[url('${IMAGES.WEBSITE.HOME_HERO}')] bg-cover bg-center`}>
|
|
603
710
|
|
|
604
|
-
//
|
|
711
|
+
// In authentication pages
|
|
605
712
|
<Image
|
|
606
713
|
src={IMAGES.AUTH.LOGIN_BG}
|
|
607
714
|
alt="Login background"
|
|
608
715
|
fill
|
|
609
716
|
className="object-cover"
|
|
610
717
|
/>
|
|
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
|
-
/>
|
|
620
718
|
```
|
|
621
719
|
|
|
622
|
-
|
|
720
|
+
**Preference Order**:
|
|
623
721
|
|
|
624
722
|
1. ✅ **SVG** - Best for logos, icons, and vector graphics (scalable, small file size)
|
|
625
|
-
2. ✅ **WebP** - Modern format for photos (
|
|
723
|
+
2. ✅ **WebP** - Modern format for photos (smaller than PNG/JPG)
|
|
626
724
|
3. ✅ **PNG** - For images requiring transparency
|
|
627
725
|
4. ✅ **JPG** - For photos without transparency
|
|
628
726
|
|
|
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
|
-
|
|
656
727
|
**❌ DO NOT**:
|
|
728
|
+
|
|
657
729
|
- Put images in `src/` folder
|
|
658
730
|
- Import images as modules unless necessary
|
|
659
731
|
- Use base64 encoded images inline
|
|
660
732
|
- Forget to optimize images before adding
|
|
661
|
-
-
|
|
662
|
-
- Export images that already exist in the project
|
|
733
|
+
- Hardcode image paths directly in components (use centralized constants)
|
|
663
734
|
|
|
664
735
|
**✅ DO**:
|
|
665
|
-
|
|
666
|
-
-
|
|
667
|
-
-
|
|
736
|
+
|
|
737
|
+
- Always place in `public/` directory
|
|
738
|
+
- Create centralized image constants file (src/config/images.ts)
|
|
739
|
+
- Organize image paths by category (LOGOS, AUTH, WEBSITE, SERVICES)
|
|
668
740
|
- Use `as const` for type safety
|
|
669
|
-
- Import from `@/config/images` in
|
|
670
|
-
- Check if image exists before exporting from Figma
|
|
741
|
+
- Import from `@/config/images` in components
|
|
671
742
|
- Prefer SVG for logos and icons
|
|
672
743
|
- Optimize images before adding (use tools like ImageOptim, Squoosh)
|
|
673
744
|
- Use Next.js `<Image>` component for automatic optimization
|
|
674
745
|
|
|
675
746
|
---
|
|
676
747
|
|
|
748
|
+
## 🎨 Color Palette & Theme
|
|
749
|
+
|
|
750
|
+
### CRITICAL: Use Theme Colors, NOT Custom Values
|
|
751
|
+
|
|
752
|
+
**Colors are defined in two places**:
|
|
753
|
+
|
|
754
|
+
1. **`src/styles/hero.ts`** - HeroUI theme colors (primary, secondary, etc.)
|
|
755
|
+
2. **`src/styles/globals.css`** - Additional custom theme colors
|
|
756
|
+
|
|
757
|
+
### Available Theme Colors
|
|
758
|
+
|
|
759
|
+
#### From hero.ts (Primary Color Palette):
|
|
760
|
+
|
|
761
|
+
```typescript
|
|
762
|
+
// Primary green shades (defined in hero.ts)
|
|
763
|
+
primary - 50; // #E6FFF0 (lightest)
|
|
764
|
+
primary - 100; // #CBFFE0
|
|
765
|
+
primary - 200; // #AEFFD1
|
|
766
|
+
primary - 300; // #8EFFC2
|
|
767
|
+
primary - 400; // #66FFB2
|
|
768
|
+
primary - 500; // #19FFA3 (default primary)
|
|
769
|
+
primary - 600; // #25C780
|
|
770
|
+
primary - 700; // #26915F
|
|
771
|
+
primary - 800; // #215F40
|
|
772
|
+
primary - 900; // #173123 (darkest)
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
#### HeroUI Default Colors:
|
|
776
|
+
|
|
777
|
+
```tsx
|
|
778
|
+
// Semantic colors (built into HeroUI)
|
|
779
|
+
bg-primary text-primary border-primary
|
|
780
|
+
bg-secondary text-secondary border-secondary
|
|
781
|
+
bg-success text-success border-success
|
|
782
|
+
bg-danger text-danger border-danger
|
|
783
|
+
bg-warning text-warning border-warning
|
|
784
|
+
bg-default text-default border-default
|
|
785
|
+
bg-foreground text-foreground border-foreground
|
|
786
|
+
bg-background text-background border-background
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
### Color Usage Examples
|
|
790
|
+
|
|
791
|
+
**✅ CORRECT - Use theme colors**:
|
|
792
|
+
|
|
793
|
+
```tsx
|
|
794
|
+
// In component configuration (NO hover states)
|
|
795
|
+
color: {
|
|
796
|
+
primary: 'bg-primary text-black',
|
|
797
|
+
success: 'bg-success text-white',
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// In className (for text colors, borders, etc.)
|
|
801
|
+
className="text-primary border-primary-500"
|
|
802
|
+
className="bg-primary-50 text-primary-900"
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
**❌ WRONG - Custom color values or hover states**:
|
|
806
|
+
|
|
807
|
+
```tsx
|
|
808
|
+
// DON'T use hex values or hover states in config
|
|
809
|
+
color: {
|
|
810
|
+
primary: 'bg-[#19ffa3] text-black hover:bg-[#00e68a]',
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// DON'T use rgb/hsl
|
|
814
|
+
className="bg-[rgb(25,255,163)]"
|
|
815
|
+
className="text-[hsl(155,100%,56%)]"
|
|
816
|
+
|
|
817
|
+
// DON'T use hover states in component config
|
|
818
|
+
color: {
|
|
819
|
+
primary: 'bg-primary text-black hover:bg-primary-600', // HeroUI handles hover
|
|
820
|
+
}
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
### When to Add Custom Colors
|
|
824
|
+
|
|
825
|
+
**Only add custom colors to hero.ts if**:
|
|
826
|
+
|
|
827
|
+
1. Color is NOT in the existing palette
|
|
828
|
+
2. Color is used in multiple places (reusable)
|
|
829
|
+
3. Color is part of the design system
|
|
830
|
+
|
|
831
|
+
**Steps to add custom color**:
|
|
832
|
+
|
|
833
|
+
```typescript
|
|
834
|
+
// 1. Add to hero.ts theme
|
|
835
|
+
export default heroui({
|
|
836
|
+
themes: {
|
|
837
|
+
extend: {
|
|
838
|
+
colors: {
|
|
839
|
+
// Add new color palette
|
|
840
|
+
accent: {
|
|
841
|
+
50: "#...",
|
|
842
|
+
500: "#...",
|
|
843
|
+
900: "#...",
|
|
844
|
+
},
|
|
845
|
+
},
|
|
846
|
+
},
|
|
847
|
+
},
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
// 2. Use in components
|
|
851
|
+
className = "bg-accent text-accent-900";
|
|
852
|
+
```
|
|
853
|
+
|
|
854
|
+
---
|
|
855
|
+
|
|
677
856
|
## 🎨 Tailwind CSS Guidelines
|
|
678
857
|
|
|
679
858
|
### Using Standard Tailwind Classes
|
|
@@ -681,21 +860,23 @@ This prevents:
|
|
|
681
860
|
**CRITICAL**: Always use closest standard Tailwind class instead of custom arbitrary values
|
|
682
861
|
|
|
683
862
|
**Common Conversions**:
|
|
863
|
+
|
|
684
864
|
```tsx
|
|
685
865
|
// ❌ WRONG - Custom arbitrary values
|
|
686
|
-
className="h-[41px] w-[54.6px]"
|
|
687
|
-
className="p-[24px]"
|
|
688
|
-
className="text-[14px]"
|
|
689
|
-
className="gap-[11px]"
|
|
866
|
+
className = "h-[41px] w-[54.6px]";
|
|
867
|
+
className = "p-[24px]";
|
|
868
|
+
className = "text-[14px]";
|
|
869
|
+
className = "gap-[11px]";
|
|
690
870
|
|
|
691
871
|
// ✅ CORRECT - Standard Tailwind classes
|
|
692
|
-
className="h-10 w-14"
|
|
693
|
-
className="p-6"
|
|
694
|
-
className="text-sm"
|
|
695
|
-
className="gap-3"
|
|
872
|
+
className = "h-10 w-14"; // h-10 = 40px, w-14 = 56px (closest to 54.6px)
|
|
873
|
+
className = "p-6"; // p-6 = 24px
|
|
874
|
+
className = "text-sm"; // text-sm = 14px
|
|
875
|
+
className = "gap-3"; // gap-3 = 12px (closest to 11px)
|
|
696
876
|
```
|
|
697
877
|
|
|
698
878
|
**Tailwind Size Reference**:
|
|
879
|
+
|
|
699
880
|
```css
|
|
700
881
|
/* Spacing Scale (padding, margin, gap, etc.) */
|
|
701
882
|
0.5 = 2px 4 = 16px 12 = 48px
|
|
@@ -719,11 +900,23 @@ auto, full, screen, fit, min, max
|
|
|
719
900
|
```
|
|
720
901
|
|
|
721
902
|
**Only use arbitrary values when**:
|
|
903
|
+
|
|
722
904
|
- Exact pixel value is required by design system
|
|
723
905
|
- No standard Tailwind class is close enough
|
|
724
906
|
- Using design tokens/CSS variables: `bg-[var(--custom-color)]`
|
|
725
907
|
|
|
908
|
+
**Examples**:
|
|
909
|
+
|
|
910
|
+
```tsx
|
|
911
|
+
// ✅ Good - Using standard classes
|
|
912
|
+
<div className="h-10 w-full p-6 text-sm">
|
|
913
|
+
|
|
914
|
+
// ❌ Bad - Should use standard classes
|
|
915
|
+
<div className="h-[40px] w-[100%] p-[24px] text-[14px]">
|
|
916
|
+
```
|
|
917
|
+
|
|
726
918
|
### Responsive Breakpoints (Mobile-First)
|
|
919
|
+
|
|
727
920
|
```css
|
|
728
921
|
/* Mobile */
|
|
729
922
|
4xs: 320px /* iPhone SE */
|
|
@@ -743,19 +936,113 @@ xl: 1280px /* MacBook Air */
|
|
|
743
936
|
4xl: 1728px /* MacBook Pro 16" */
|
|
744
937
|
```
|
|
745
938
|
|
|
939
|
+
### Usage Example
|
|
940
|
+
|
|
941
|
+
```tsx
|
|
942
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
943
|
+
{/* Mobile: 1 column, Tablet: 2 columns, Desktop: 3 columns */}
|
|
944
|
+
</div>
|
|
945
|
+
```
|
|
946
|
+
|
|
746
947
|
### HeroUI Color System
|
|
747
948
|
|
|
748
949
|
Use these semantic colors from HeroUI theme:
|
|
950
|
+
|
|
749
951
|
- `bg-background` - Main background
|
|
750
952
|
- `bg-foreground` - Text color background
|
|
751
953
|
- `bg-default` / `bg-default-50` to `bg-default-900` - Gray scale
|
|
752
|
-
- `bg-primary` / `text-primary` - Primary brand color
|
|
753
|
-
- `bg-primary-50` to `bg-primary-900` - Primary color shades
|
|
954
|
+
- `bg-primary` / `text-primary` - Primary brand color
|
|
754
955
|
- `bg-secondary` - Secondary color
|
|
755
956
|
- `bg-success` - Success states
|
|
756
957
|
- `bg-warning` - Warning states
|
|
757
958
|
- `bg-danger` - Error/danger states
|
|
758
959
|
|
|
960
|
+
### Common Patterns
|
|
961
|
+
|
|
962
|
+
```tsx
|
|
963
|
+
// Card
|
|
964
|
+
<div className="rounded-lg bg-white p-6 shadow-md">
|
|
965
|
+
|
|
966
|
+
// Container
|
|
967
|
+
<div className="container mx-auto px-4">
|
|
968
|
+
|
|
969
|
+
// Flexbox centering
|
|
970
|
+
<div className="flex items-center justify-center">
|
|
971
|
+
|
|
972
|
+
// Grid layout
|
|
973
|
+
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
974
|
+
|
|
975
|
+
// Spacing
|
|
976
|
+
<div className="space-y-4"> {/* Vertical spacing */}
|
|
977
|
+
<div className="space-x-4"> {/* Horizontal spacing */}
|
|
978
|
+
```
|
|
979
|
+
|
|
980
|
+
---
|
|
981
|
+
|
|
982
|
+
## 📝 Form Implementation with Zod
|
|
983
|
+
|
|
984
|
+
### Pattern to Follow
|
|
985
|
+
|
|
986
|
+
```tsx
|
|
987
|
+
"use client";
|
|
988
|
+
|
|
989
|
+
import { zodResolver } from "@hookform/resolvers/zod";
|
|
990
|
+
import { useForm } from "react-hook-form";
|
|
991
|
+
import { z } from "zod";
|
|
992
|
+
|
|
993
|
+
import { Button, Input } from "@/components/ui";
|
|
994
|
+
|
|
995
|
+
// 1. Define schema
|
|
996
|
+
const schema = z.object({
|
|
997
|
+
email: z.string().email("Invalid email"),
|
|
998
|
+
password: z.string().min(8, "Password must be at least 8 characters"),
|
|
999
|
+
});
|
|
1000
|
+
|
|
1001
|
+
type FormData = z.infer<typeof schema>;
|
|
1002
|
+
|
|
1003
|
+
export default function LoginForm() {
|
|
1004
|
+
// 2. Setup form
|
|
1005
|
+
const {
|
|
1006
|
+
register,
|
|
1007
|
+
handleSubmit,
|
|
1008
|
+
formState: { errors, isSubmitting },
|
|
1009
|
+
} = useForm<FormData>({
|
|
1010
|
+
resolver: zodResolver(schema),
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
// 3. Submit handler
|
|
1014
|
+
const onSubmit = async (data: FormData) => {
|
|
1015
|
+
// Handle form submission
|
|
1016
|
+
console.log(data);
|
|
1017
|
+
};
|
|
1018
|
+
|
|
1019
|
+
// 4. Render form
|
|
1020
|
+
return (
|
|
1021
|
+
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
|
|
1022
|
+
<Input
|
|
1023
|
+
label="Email"
|
|
1024
|
+
type="email"
|
|
1025
|
+
{...register("email")}
|
|
1026
|
+
errorMessage={errors.email?.message}
|
|
1027
|
+
isInvalid={!!errors.email}
|
|
1028
|
+
/>
|
|
1029
|
+
|
|
1030
|
+
<Input
|
|
1031
|
+
label="Password"
|
|
1032
|
+
type="password"
|
|
1033
|
+
{...register("password")}
|
|
1034
|
+
errorMessage={errors.password?.message}
|
|
1035
|
+
isInvalid={!!errors.password}
|
|
1036
|
+
/>
|
|
1037
|
+
|
|
1038
|
+
<Button type="submit" isLoading={isSubmitting} className="w-full">
|
|
1039
|
+
Submit
|
|
1040
|
+
</Button>
|
|
1041
|
+
</form>
|
|
1042
|
+
);
|
|
1043
|
+
}
|
|
1044
|
+
```
|
|
1045
|
+
|
|
759
1046
|
---
|
|
760
1047
|
|
|
761
1048
|
## 🌐 Internationalization (i18n)
|
|
@@ -765,18 +1052,20 @@ Use these semantic colors from HeroUI theme:
|
|
|
765
1052
|
### Navigation with i18n
|
|
766
1053
|
|
|
767
1054
|
**Import Guidelines**:
|
|
1055
|
+
|
|
768
1056
|
```tsx
|
|
769
1057
|
// For Link component - use Next.js Link
|
|
770
|
-
import Link from
|
|
1058
|
+
import Link from "next/link";
|
|
771
1059
|
|
|
772
1060
|
// For navigation hooks - use next/navigation
|
|
773
|
-
import { usePathname, useRouter } from
|
|
1061
|
+
import { usePathname, useRouter } from "next/navigation";
|
|
774
1062
|
|
|
775
1063
|
// For navigation URLs - use centralized constants
|
|
776
|
-
import { NAVIGATION_URLS } from
|
|
1064
|
+
import { NAVIGATION_URLS } from "@/config/navigationUrls";
|
|
777
1065
|
```
|
|
778
1066
|
|
|
779
1067
|
**Navigation Example**:
|
|
1068
|
+
|
|
780
1069
|
```tsx
|
|
781
1070
|
import Link from 'next/link';
|
|
782
1071
|
import { NAVIGATION_URLS } from '@/config/navigationUrls';
|
|
@@ -792,9 +1081,29 @@ import { Link } from '@heroui/react';
|
|
|
792
1081
|
<Link href={NAVIGATION_URLS.ABOUT}>About</Link>
|
|
793
1082
|
```
|
|
794
1083
|
|
|
1084
|
+
**Using Navigation Hooks**:
|
|
1085
|
+
|
|
1086
|
+
```tsx
|
|
1087
|
+
"use client";
|
|
1088
|
+
|
|
1089
|
+
import { usePathname, useRouter } from "next/navigation";
|
|
1090
|
+
|
|
1091
|
+
export function MyComponent() {
|
|
1092
|
+
const pathname = usePathname();
|
|
1093
|
+
const router = useRouter();
|
|
1094
|
+
|
|
1095
|
+
const handleNavigation = () => {
|
|
1096
|
+
router.push(NAVIGATION_URLS.DASHBOARD.INDEX);
|
|
1097
|
+
};
|
|
1098
|
+
|
|
1099
|
+
return <button onClick={handleNavigation}>Go to Dashboard</button>;
|
|
1100
|
+
}
|
|
1101
|
+
```
|
|
1102
|
+
|
|
795
1103
|
### Translations for ALL text
|
|
1104
|
+
|
|
796
1105
|
```tsx
|
|
797
|
-
import { useTranslations } from
|
|
1106
|
+
import { useTranslations } from "next-intl";
|
|
798
1107
|
|
|
799
1108
|
function MyComponent() {
|
|
800
1109
|
const t = useTranslations();
|
|
@@ -802,8 +1111,8 @@ function MyComponent() {
|
|
|
802
1111
|
return (
|
|
803
1112
|
<div>
|
|
804
1113
|
{/* ✅ CORRECT - Using translation key */}
|
|
805
|
-
<h1>{t(
|
|
806
|
-
<p>{t(
|
|
1114
|
+
<h1>{t("welcome")}</h1>
|
|
1115
|
+
<p>{t("description")}</p>
|
|
807
1116
|
|
|
808
1117
|
{/* ❌ WRONG - Hardcoded text */}
|
|
809
1118
|
<h1>Welcome</h1>
|
|
@@ -812,33 +1121,166 @@ function MyComponent() {
|
|
|
812
1121
|
}
|
|
813
1122
|
```
|
|
814
1123
|
|
|
1124
|
+
### Form labels, placeholders, errors - ALL must use i18n
|
|
1125
|
+
|
|
1126
|
+
```tsx
|
|
1127
|
+
import { useTranslations } from "next-intl";
|
|
1128
|
+
|
|
1129
|
+
function LoginForm() {
|
|
1130
|
+
const t = useTranslations();
|
|
1131
|
+
|
|
1132
|
+
return (
|
|
1133
|
+
<form>
|
|
1134
|
+
{/* ✅ CORRECT */}
|
|
1135
|
+
<Input
|
|
1136
|
+
label={t("email")}
|
|
1137
|
+
placeholder={t("enterEmail")}
|
|
1138
|
+
errorMessage={
|
|
1139
|
+
errors.email?.message ? t(errors.email.message) : undefined
|
|
1140
|
+
}
|
|
1141
|
+
/>
|
|
1142
|
+
|
|
1143
|
+
{/* ❌ WRONG - Hardcoded text */}
|
|
1144
|
+
<Input
|
|
1145
|
+
label="Email"
|
|
1146
|
+
placeholder="Enter your email"
|
|
1147
|
+
errorMessage="Invalid email"
|
|
1148
|
+
/>
|
|
1149
|
+
</form>
|
|
1150
|
+
);
|
|
1151
|
+
}
|
|
1152
|
+
```
|
|
1153
|
+
|
|
1154
|
+
### Navigation URLs Reference
|
|
1155
|
+
|
|
1156
|
+
```tsx
|
|
1157
|
+
import { NAVIGATION_URLS } from '@/config/navigationUrls';
|
|
1158
|
+
|
|
1159
|
+
// Available URLs (based on project type):
|
|
1160
|
+
NAVIGATION_URLS.ROOT // '/'
|
|
1161
|
+
NAVIGATION_URLS.ABOUT // '/about'
|
|
1162
|
+
NAVIGATION_URLS.CONTACT // '/contact'
|
|
1163
|
+
NAVIGATION_URLS.PRODUCTS.INDEX // '/products'
|
|
1164
|
+
NAVIGATION_URLS.PRODUCTS.DETAIL('123') // '/products/123'
|
|
1165
|
+
NAVIGATION_URLS.AUTH.LOGIN // '/auth/login'
|
|
1166
|
+
NAVIGATION_URLS.DASHBOARD.INDEX // '/dashboard'
|
|
1167
|
+
|
|
1168
|
+
// ✅ ALWAYS use NAVIGATION_URLS
|
|
1169
|
+
<Link href={NAVIGATION_URLS.PRODUCTS.INDEX}>Products</Link>
|
|
1170
|
+
|
|
1171
|
+
// ❌ NEVER hardcode URLs
|
|
1172
|
+
<Link href="/products">Products</Link>
|
|
1173
|
+
```
|
|
1174
|
+
|
|
1175
|
+
---
|
|
1176
|
+
|
|
1177
|
+
## 🎯 Data Fetching
|
|
1178
|
+
|
|
1179
|
+
### Using React Query
|
|
1180
|
+
|
|
1181
|
+
```tsx
|
|
1182
|
+
"use client";
|
|
1183
|
+
|
|
1184
|
+
import { useQuery } from "@tanstack/react-query";
|
|
1185
|
+
import axios from "axios";
|
|
1186
|
+
|
|
1187
|
+
import { QUERY_KEYS } from "@/libs/react-query";
|
|
1188
|
+
|
|
1189
|
+
export default function ProductList() {
|
|
1190
|
+
const { data, isLoading, error } = useQuery({
|
|
1191
|
+
queryKey: QUERY_KEYS.PRODUCTS.LIST,
|
|
1192
|
+
queryFn: async () => {
|
|
1193
|
+
const response = await axios.get("/api/products");
|
|
1194
|
+
return response.data;
|
|
1195
|
+
},
|
|
1196
|
+
});
|
|
1197
|
+
|
|
1198
|
+
if (isLoading) return <div>Loading...</div>;
|
|
1199
|
+
if (error) return <div>Error loading products</div>;
|
|
1200
|
+
|
|
1201
|
+
return (
|
|
1202
|
+
<div>
|
|
1203
|
+
{data.map((product) => (
|
|
1204
|
+
<div key={product.id}>{product.name}</div>
|
|
1205
|
+
))}
|
|
1206
|
+
</div>
|
|
1207
|
+
);
|
|
1208
|
+
}
|
|
1209
|
+
```
|
|
1210
|
+
|
|
1211
|
+
---
|
|
1212
|
+
|
|
1213
|
+
## 📐 Layout Components
|
|
1214
|
+
|
|
1215
|
+
### Using Existing Layouts
|
|
1216
|
+
|
|
1217
|
+
```tsx
|
|
1218
|
+
// Auth pages use auth layout automatically
|
|
1219
|
+
// Location: src/app/[locale]/(auth)/login/page.tsx
|
|
1220
|
+
|
|
1221
|
+
"use client";
|
|
1222
|
+
|
|
1223
|
+
export default function LoginPage() {
|
|
1224
|
+
return (
|
|
1225
|
+
<div className="w-full max-w-md space-y-6 rounded-lg bg-white p-8 shadow-md">
|
|
1226
|
+
<h1 className="text-center text-2xl font-bold">Login</h1>
|
|
1227
|
+
{/* Your form here */}
|
|
1228
|
+
</div>
|
|
1229
|
+
);
|
|
1230
|
+
}
|
|
1231
|
+
```
|
|
1232
|
+
|
|
1233
|
+
### Navbar & Footer
|
|
1234
|
+
|
|
1235
|
+
Already implemented in `src/components/layout/`:
|
|
1236
|
+
|
|
1237
|
+
- `Navbar.tsx` - Main navigation using HeroUI Navbar
|
|
1238
|
+
- `Footer.tsx` - Footer component
|
|
1239
|
+
|
|
1240
|
+
**CRITICAL**: Navbar uses HeroUI Navbar component:
|
|
1241
|
+
|
|
1242
|
+
```tsx
|
|
1243
|
+
import { Navbar } from '@/components/layout';
|
|
1244
|
+
|
|
1245
|
+
// In layout:
|
|
1246
|
+
<Navbar />
|
|
1247
|
+
<main>{children}</main>
|
|
1248
|
+
```
|
|
1249
|
+
|
|
1250
|
+
**Implementation details** (see `src/components/layout/Navbar.tsx`):
|
|
1251
|
+
|
|
1252
|
+
- ✅ Uses HeroUI `Navbar`, `NavbarBrand`, `NavbarContent`, `NavbarItem`
|
|
1253
|
+
- ✅ Uses `Button` from `@/components/ui` with props (`color`, `size`, `variant`)
|
|
1254
|
+
- ✅ All styles via props, NO className for component styles
|
|
1255
|
+
- ✅ className ONLY for layout utilities
|
|
1256
|
+
|
|
815
1257
|
---
|
|
816
1258
|
|
|
817
1259
|
## ⚠️ CRITICAL RULES
|
|
818
1260
|
|
|
819
1261
|
### ❌ DO NOT
|
|
820
1262
|
|
|
821
|
-
1. ❌ **Figma
|
|
822
|
-
2. ❌ **Figma
|
|
823
|
-
3. ❌ **Icons**:
|
|
824
|
-
4. ❌ **Icons**:
|
|
825
|
-
5. ❌ **Icons**:
|
|
826
|
-
6. ❌ **Logos**: Put logos/images in `src/` folder - MUST go in `public
|
|
1263
|
+
1. ❌ **Figma**: Use screenshots to implement design - MUST use Figma MCP server with `get_design_context`
|
|
1264
|
+
2. ❌ **Figma**: Call `get_screenshot` tool - Design context provides all needed information
|
|
1265
|
+
3. ❌ **Icons**: Import individual icon components (e.g., `import { ChevronDownIcon }`) - MUST use `<Icon name="ChevronDownIcon" />`
|
|
1266
|
+
4. ❌ **Icons**: Create manual icon components - MUST use `pnpm generate-icons` workflow
|
|
1267
|
+
5. ❌ **Icons**: Use non-project icons (Lucide, Font Awesome, etc.)
|
|
1268
|
+
6. ❌ **Logos**: Put logos/images in `src/` folder - MUST go in `public/`
|
|
827
1269
|
7. ❌ **Logos**: Import images as modules unless necessary
|
|
828
|
-
8. ❌ **Logos**: Hardcode image paths in components - MUST use `@/config/images`
|
|
829
|
-
9. ❌ **
|
|
830
|
-
10. ❌ **
|
|
831
|
-
11. ❌ **Colors**:
|
|
832
|
-
12. ❌ **
|
|
833
|
-
13. ❌ **
|
|
834
|
-
14. ❌ **
|
|
835
|
-
15. ❌ **
|
|
836
|
-
16. ❌ **
|
|
837
|
-
17. ❌ **Components**:
|
|
838
|
-
18. ❌ **
|
|
839
|
-
19. ❌ **Styling**:
|
|
1270
|
+
8. ❌ **Logos**: Hardcode image paths in components - MUST use centralized constants from `@/config/images`
|
|
1271
|
+
9. ❌ **Tailwind**: Use arbitrary values when standard classes exist (e.g., `h-[40px]` → use `h-10`)
|
|
1272
|
+
10. ❌ **Tailwind**: Use custom values like `w-[100%]` (use `w-full`)
|
|
1273
|
+
11. ❌ **Colors**: Use custom color values (e.g., `bg-[#19ffa3]`, `text-[#ff0000]`) - MUST use theme colors from hero.ts
|
|
1274
|
+
12. ❌ **Colors**: Use hex, rgb, or hsl values inline - Use semantic color classes instead
|
|
1275
|
+
13. ❌ **Text**: Hardcode ANY text - ALL text MUST use i18n translation keys
|
|
1276
|
+
14. ❌ **URLs**: Hardcode URLs - ALWAYS use `NAVIGATION_URLS` from config
|
|
1277
|
+
15. ❌ **Data**: Hardcode menu items, dropdown options - MUST map from constants
|
|
1278
|
+
16. ❌ **Components**: Create custom components when HeroUI provides them (e.g., use HeroUI `Navbar` not custom `<header>`)
|
|
1279
|
+
17. ❌ **Components**: Use HTML elements when HeroUI component exists (e.g., `<button>` → use HeroUI `<Button>`)
|
|
1280
|
+
18. ❌ **Styling**: Apply component styles via className - MUST use props (`color`, `size`, `variant`, `radius`)
|
|
1281
|
+
19. ❌ **Styling**: Use className for component styles (e.g., `className="h-10 px-4 bg-primary"`) - Configure in wrapper instead
|
|
840
1282
|
20. ❌ **Styling**: Use CSS modules or styled-components - ONLY Tailwind CSS
|
|
841
|
-
21. ❌ **Imports**: Import from `@heroui/react` directly (use `@/components/ui` wrappers)
|
|
1283
|
+
21. ❌ **Imports**: Import from `@heroui/react` directly (use `@/components/ui` wrappers when available)
|
|
842
1284
|
22. ❌ **Navigation**: Use HeroUI `Link` for internal navigation (use Next.js `Link` from `next/link`)
|
|
843
1285
|
23. ❌ **Navigation**: Import `usePathname`, `useRouter` from wrong source (MUST use `next/navigation`)
|
|
844
1286
|
24. ❌ **TypeScript**: Use `any` type
|
|
@@ -847,42 +1289,38 @@ function MyComponent() {
|
|
|
847
1289
|
|
|
848
1290
|
### ✅ DO
|
|
849
1291
|
|
|
850
|
-
1. ✅ **Figma
|
|
851
|
-
2. ✅ **Figma
|
|
852
|
-
3. ✅ **
|
|
853
|
-
4. ✅ **
|
|
854
|
-
5. ✅ **
|
|
855
|
-
6. ✅ **
|
|
856
|
-
7. ✅ **
|
|
857
|
-
8. ✅ **Logos**:
|
|
858
|
-
9. ✅ **Logos**:
|
|
859
|
-
10. ✅ **Logos**:
|
|
860
|
-
11. ✅ **
|
|
861
|
-
12. ✅ **
|
|
862
|
-
13. ✅ **
|
|
863
|
-
14. ✅ **Colors**:
|
|
864
|
-
15. ✅ **
|
|
865
|
-
16. ✅ **
|
|
866
|
-
17. ✅ **
|
|
867
|
-
18. ✅ **
|
|
868
|
-
19. ✅ **
|
|
869
|
-
20. ✅ **
|
|
870
|
-
21. ✅ **
|
|
871
|
-
22. ✅ **
|
|
872
|
-
23. ✅ **
|
|
873
|
-
24. ✅ **
|
|
874
|
-
25. ✅ **
|
|
875
|
-
26. ✅ **
|
|
876
|
-
27. ✅ **
|
|
877
|
-
28. ✅ **
|
|
878
|
-
29. ✅ **
|
|
879
|
-
30. ✅ **
|
|
880
|
-
31. ✅ **
|
|
881
|
-
32. ✅ **
|
|
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
|
|
1292
|
+
1. ✅ **Figma**: Use Figma MCP server with `get_design_context` and node-id from Figma URL
|
|
1293
|
+
2. ✅ **Figma**: Extract node-id from URL (e.g., `node-id=8486-1580` → `"8486:1580"`)
|
|
1294
|
+
3. ✅ **Icons**: Export SVG from Figma → `src/assets/icons/` → Run `pnpm generate-icons`
|
|
1295
|
+
4. ✅ **Icons**: Use `<Icon name="IconName" />` pattern with PascalCase - NEVER import individual icon components
|
|
1296
|
+
5. ✅ **Logos**: Check `public/images/index.ts` BEFORE exporting from Figma
|
|
1297
|
+
6. ✅ **Logos**: Check `public/images/logos/` directory for existing assets
|
|
1298
|
+
7. ✅ **Logos**: Export SVG/PNG from Figma ONLY if not existing → Place in `public/images/` with organized folders
|
|
1299
|
+
8. ✅ **Logos**: Create centralized constants file `src/config/images.ts` with LOGOS, AUTH, WEBSITE, etc.
|
|
1300
|
+
9. ✅ **Logos**: Import from `@/config/images` and use `IMAGES.LOGOS.PRIMARY`
|
|
1301
|
+
10. ✅ **Logos**: Prefer SVG over PNG when source is vector
|
|
1302
|
+
11. ✅ **Tailwind**: Use standard classes (`h-10`, `p-6`, `text-sm` instead of arbitrary values)
|
|
1303
|
+
12. ✅ **Tailwind**: Use size utilities (`size-4`, `size-5`) for icons
|
|
1304
|
+
13. ✅ **Colors**: Use theme colors from hero.ts (`bg-primary`, `text-primary-600`, `bg-success`)
|
|
1305
|
+
14. ✅ **Colors**: Check hero.ts and globals.css for available colors before using custom values
|
|
1306
|
+
15. ✅ **Navigation**: Use Next.js `Link` from `next/link` for internal navigation
|
|
1307
|
+
16. ✅ **Navigation**: Import `usePathname`, `useRouter` from `next/navigation`
|
|
1308
|
+
17. ✅ **Text**: Use i18n translation keys for ALL text (`t('key')`)
|
|
1309
|
+
18. ✅ **URLs**: Use `NAVIGATION_URLS` for ALL navigation links
|
|
1310
|
+
19. ✅ **Data**: Map menu items, dropdown options from constants (never hardcode)
|
|
1311
|
+
20. ✅ **Components**: Use HeroUI components first (e.g., use `Navbar` from `@heroui/react` for navigation)
|
|
1312
|
+
21. ✅ **Components**: Use existing wrappers from `src/components/ui/` when available (e.g., `Button`)
|
|
1313
|
+
22. ✅ **Components**: Configure component styles in wrappers using `extendVariants`
|
|
1314
|
+
23. ✅ **Components**: Set `defaultVariants` in wrapper configuration to avoid repetition
|
|
1315
|
+
24. ✅ **Styling**: Use component props for styles, but ONLY specify non-default values
|
|
1316
|
+
25. ✅ **Styling**: Omit props that match defaultVariants (e.g., don't write `size="md"` if `md` is default)
|
|
1317
|
+
26. ✅ **Styling**: Use className ONLY for layout utilities (`mt-4`, `hidden`, `lg:flex`)
|
|
1318
|
+
27. ✅ **Styling**: Use Tailwind CSS ONLY (utility classes)
|
|
1319
|
+
28. ✅ **TypeScript**: Implement strict typing with proper types
|
|
1320
|
+
29. ✅ **React**: Use `'use client'` directive for interactive components
|
|
1321
|
+
30. ✅ **Design**: Follow mobile-first responsive design
|
|
1322
|
+
31. ✅ **States**: Implement proper loading and error states
|
|
1323
|
+
32. ✅ **Patterns**: Follow existing code patterns and naming conventions
|
|
886
1324
|
|
|
887
1325
|
---
|
|
888
1326
|
|
|
@@ -890,78 +1328,61 @@ function MyComponent() {
|
|
|
890
1328
|
|
|
891
1329
|
When converting Figma to code, ensure:
|
|
892
1330
|
|
|
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
1331
|
### Icons & Assets
|
|
1332
|
+
|
|
901
1333
|
- [ ] Exported icons as SVG from Figma
|
|
902
1334
|
- [ ] Placed SVG files in `src/assets/icons/`
|
|
903
1335
|
- [ ] Ran `pnpm generate-icons` to create React components
|
|
904
|
-
- [ ]
|
|
905
|
-
- [ ] Checked
|
|
906
|
-
- [ ]
|
|
1336
|
+
- [ ] Imported icons from `@/components/icons`
|
|
1337
|
+
- [ ] **BEFORE exporting**: Checked `public/images/index.ts` for existing images/logos
|
|
1338
|
+
- [ ] **BEFORE exporting**: Checked `public/images/logos/` directory
|
|
1339
|
+
- [ ] Exported logos/images as SVG (preferred) or PNG (only if not existing)
|
|
907
1340
|
- [ ] Placed logos/images in `public/images/` with organized folders (logos/, auth/, website/, services/)
|
|
908
|
-
- [ ] Created
|
|
1341
|
+
- [ ] Created centralized constants file `src/config/images.ts`
|
|
909
1342
|
- [ ] Organized image paths by category (LOGOS, AUTH, WEBSITE, SERVICES, PRODUCTS)
|
|
910
1343
|
- [ ] Used `as const` for type safety
|
|
911
|
-
- [ ] Referenced via `IMAGES.
|
|
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
|
|
1344
|
+
- [ ] Referenced via `IMAGES.LOGOS.PRIMARY` (not hardcoded paths)
|
|
918
1345
|
|
|
919
1346
|
### Component & Styling
|
|
1347
|
+
|
|
920
1348
|
- [ ] Checked HeroUI library first for component availability
|
|
921
|
-
- [ ] Used HeroUI components via `src/components/ui/` wrappers
|
|
922
|
-
- [ ]
|
|
923
|
-
- [ ] Kept component usage clean (no repetitive className props)
|
|
1349
|
+
- [ ] Used HeroUI components via `src/components/ui/` wrappers (e.g., `<Button>` not `<button>`)
|
|
1350
|
+
- [ ] Configured component styles in wrapper/theme (not inline)
|
|
924
1351
|
- [ ] Used only layout utilities inline (`mt-4`, `hidden`, `lg:block`)
|
|
925
1352
|
- [ ] Applied Tailwind CSS classes only (no custom CSS, no CSS modules)
|
|
926
|
-
- [ ] Used standard Tailwind classes instead of arbitrary values
|
|
1353
|
+
- [ ] Used standard Tailwind classes instead of arbitrary values (e.g., `h-10` not `h-[40px]`)
|
|
927
1354
|
- [ ] Followed HeroUI theme colors (bg-background, bg-primary, etc.)
|
|
928
1355
|
- [ ] Mapped all data from constants (no hardcoded menu items/dropdowns)
|
|
929
|
-
- [ ] Created constants files for navigation items and menu options
|
|
930
1356
|
|
|
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`
|
|
1357
|
+
### Content & Navigation
|
|
937
1358
|
|
|
938
|
-
### Content & i18n
|
|
939
1359
|
- [ ] ALL text uses i18n translation keys (no hardcoded text)
|
|
940
1360
|
- [ ] ALL labels use `t('labelKey')`
|
|
941
1361
|
- [ ] ALL placeholders use `t('placeholderKey')`
|
|
942
1362
|
- [ ] ALL error messages use `t('errorKey')`
|
|
943
1363
|
- [ ] ALL titles and headings use `t('titleKey')`
|
|
944
|
-
- [ ] ALL
|
|
1364
|
+
- [ ] ALL URLs use `NAVIGATION_URLS` from config (no hardcoded URLs)
|
|
945
1365
|
|
|
946
1366
|
### Structure & Types
|
|
1367
|
+
|
|
947
1368
|
- [ ] Page created in correct location (`src/app/[locale]/...`)
|
|
948
1369
|
- [ ] Added TypeScript types for all data structures
|
|
949
1370
|
- [ ] Used `'use client'` for interactive components
|
|
950
1371
|
- [ ] No `any` types used
|
|
951
|
-
- [ ] Used `as const` for constants type safety
|
|
952
1372
|
|
|
953
1373
|
### Forms & Validation
|
|
1374
|
+
|
|
954
1375
|
- [ ] Implemented forms with Zod validation schemas
|
|
955
1376
|
- [ ] Form labels use i18n
|
|
956
1377
|
- [ ] Form placeholders use i18n
|
|
957
1378
|
- [ ] Form errors use i18n
|
|
958
1379
|
|
|
959
1380
|
### Responsive & States
|
|
1381
|
+
|
|
960
1382
|
- [ ] Implemented responsive design (mobile-first)
|
|
961
1383
|
- [ ] Added proper loading states
|
|
962
1384
|
- [ ] Added proper error states
|
|
963
1385
|
- [ ] Tested on multiple breakpoints (mobile, tablet, desktop)
|
|
964
|
-
- [ ] Implemented responsive navigation menu
|
|
965
1386
|
|
|
966
1387
|
---
|
|
967
1388
|
|
|
@@ -969,73 +1390,59 @@ When converting Figma to code, ensure:
|
|
|
969
1390
|
|
|
970
1391
|
Before finalizing code, verify:
|
|
971
1392
|
|
|
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
1393
|
### Icons & Assets
|
|
1394
|
+
|
|
978
1395
|
1. **Icons workflow** - All icons created via `pnpm generate-icons` (not manual)
|
|
979
|
-
2. **
|
|
980
|
-
3. **
|
|
981
|
-
4. **
|
|
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)
|
|
1396
|
+
2. **Icons source** - SVG files exist in `src/assets/icons/`
|
|
1397
|
+
3. **Logos location** - All logos/images in `public/` directory (not `src/`)
|
|
1398
|
+
4. **Image format** - SVG used for logos when possible (not PNG)
|
|
992
1399
|
|
|
993
1400
|
### Components & Styling
|
|
994
|
-
|
|
1401
|
+
|
|
1402
|
+
1. **HeroUI components first** - Verify HeroUI library was checked before custom implementation
|
|
995
1403
|
2. **No direct HeroUI imports** - Only through `@/components/ui`
|
|
996
|
-
3. **Component
|
|
997
|
-
4. **
|
|
998
|
-
5. **
|
|
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
|
|
1404
|
+
3. **Component reuse** - Don't duplicate existing components
|
|
1405
|
+
4. **Use HeroUI components** - No HTML elements when HeroUI component exists (e.g., use `<Button>` not `<button>`)
|
|
1406
|
+
5. **Configure, don't inline** - Component styles in wrapper/theme, not inline classNames
|
|
1407
|
+
6. **Standard Tailwind classes** - No arbitrary values when standard class exists (e.g., `h-10` not `h-[40px]`)
|
|
1408
|
+
7. **Map from constants** - All menu items, dropdown options mapped from config files
|
|
1013
1409
|
|
|
1014
|
-
###
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1410
|
+
### i18n & Navigation
|
|
1411
|
+
|
|
1412
|
+
5. **Zero hardcoded text** - ALL text uses `t('key')`
|
|
1413
|
+
6. **Zero hardcoded URLs** - ALL links use `NAVIGATION_URLS`
|
|
1414
|
+
7. **Form fields i18n** - Labels, placeholders, errors all use translation keys
|
|
1018
1415
|
|
|
1019
|
-
###
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1416
|
+
### Styling & Layout
|
|
1417
|
+
|
|
1418
|
+
8. **Tailwind only** - No CSS modules, no styled-components
|
|
1419
|
+
9. **HeroUI theme colors** - Use semantic colors, not arbitrary values
|
|
1420
|
+
10. **Consistent spacing** - Use `space-y-*` and `space-x-*` utilities
|
|
1421
|
+
11. **Mobile-first** - Base styles for mobile, then `md:`, `lg:`, etc.
|
|
1422
|
+
|
|
1423
|
+
### TypeScript & Validation
|
|
1424
|
+
|
|
1425
|
+
12. **Proper TypeScript** - No `any`, all props typed
|
|
1426
|
+
13. **Form validation** - Zod schemas for all forms
|
|
1023
1427
|
|
|
1024
1428
|
---
|
|
1025
1429
|
|
|
1026
|
-
##
|
|
1430
|
+
## 📚 Reference Files
|
|
1027
1431
|
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1432
|
+
When in doubt, check these example files:
|
|
1433
|
+
|
|
1434
|
+
- **Form example**: `src/components/ui/form/Form.tsx`
|
|
1435
|
+
- **Page example**: `src/app/[locale]/(auth)/login/page.tsx`
|
|
1436
|
+
- **Component example**: `src/components/ui/Input.tsx`
|
|
1437
|
+
- **Styles reference**: `src/styles/globals.css`
|
|
1438
|
+
- **Theme config**: `src/styles/hero.ts`
|
|
1439
|
+
|
|
1440
|
+
---
|
|
1441
|
+
|
|
1442
|
+
## 🚀 Quick Reference Guide
|
|
1037
1443
|
|
|
1038
1444
|
### Icons Workflow
|
|
1445
|
+
|
|
1039
1446
|
```bash
|
|
1040
1447
|
# 1. Export SVG from Figma
|
|
1041
1448
|
# 2. Place in folder
|
|
@@ -1044,30 +1451,20 @@ mv icon.svg src/assets/icons/
|
|
|
1044
1451
|
# 3. Generate components
|
|
1045
1452
|
pnpm generate-icons
|
|
1046
1453
|
|
|
1047
|
-
# 4. Use
|
|
1048
|
-
import {
|
|
1049
|
-
<
|
|
1050
|
-
|
|
1051
|
-
# OR use direct import
|
|
1052
|
-
import { MenuIcon } from '@/components/icons';
|
|
1053
|
-
<MenuIcon className="size-6" />
|
|
1454
|
+
# 4. Use in code
|
|
1455
|
+
import { IconIcon } from '@/components/icons';
|
|
1456
|
+
<IconIcon className="size-5" />
|
|
1054
1457
|
```
|
|
1055
1458
|
|
|
1056
1459
|
### 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
1460
|
|
|
1064
|
-
|
|
1461
|
+
```bash
|
|
1462
|
+
# 1. Export SVG/PNG from Figma
|
|
1463
|
+
# 2. Place in public with organized folders
|
|
1065
1464
|
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
1465
|
|
|
1466
|
+
# 3. Create centralized constants file
|
|
1467
|
+
# src/config/images.ts
|
|
1071
1468
|
const LOGOS = {
|
|
1072
1469
|
PRIMARY: '/images/logos/logo-primary.svg',
|
|
1073
1470
|
ICON: '/images/logos/logo-icon.svg',
|
|
@@ -1082,63 +1479,15 @@ export const IMAGES = {
|
|
|
1082
1479
|
WEBSITE,
|
|
1083
1480
|
} as const;
|
|
1084
1481
|
|
|
1085
|
-
#
|
|
1482
|
+
# 4. Use in code
|
|
1086
1483
|
import { IMAGES } from '@/config/images';
|
|
1087
1484
|
<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>
|
|
1485
|
+
# OR
|
|
1486
|
+
<Image src={IMAGES.LOGOS.PRIMARY} alt="Logo" width={120} height={40} />
|
|
1139
1487
|
```
|
|
1140
1488
|
|
|
1141
1489
|
### Tailwind Standard Classes
|
|
1490
|
+
|
|
1142
1491
|
```tsx
|
|
1143
1492
|
// Heights/Widths
|
|
1144
1493
|
h-10 = 40px w-14 = 56px size-5 = 20px
|
|
@@ -1154,32 +1503,49 @@ text-base = 16px text-2xl = 24px
|
|
|
1154
1503
|
text-lg = 18px text-3xl = 30px
|
|
1155
1504
|
|
|
1156
1505
|
// Use standard class if within 2-4px of target
|
|
1506
|
+
// Only use arbitrary [value] for design tokens
|
|
1157
1507
|
```
|
|
1158
1508
|
|
|
1159
|
-
###
|
|
1509
|
+
### Common Patterns
|
|
1510
|
+
|
|
1160
1511
|
```tsx
|
|
1161
|
-
//
|
|
1162
|
-
|
|
1163
|
-
<
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1512
|
+
// Icons (auto-generated)
|
|
1513
|
+
import { MenuIcon, CloseIcon } from '@/components/icons';
|
|
1514
|
+
<MenuIcon className="size-6 text-white" />
|
|
1515
|
+
|
|
1516
|
+
// Logos (centralized constants)
|
|
1517
|
+
import { IMAGES } from '@/config/images';
|
|
1518
|
+
<img src={IMAGES.LOGOS.PRIMARY} alt="Logo" className="h-10 w-auto" />
|
|
1519
|
+
|
|
1520
|
+
// Navigation (Next.js Link + constants)
|
|
1521
|
+
import Link from 'next/link';
|
|
1522
|
+
import { NAVIGATION_URLS } from '@/config/navigationUrls';
|
|
1523
|
+
<Link href={NAVIGATION_URLS.ABOUT}>{t('about')}</Link>
|
|
1524
|
+
|
|
1525
|
+
// Navigation hooks (from next/navigation)
|
|
1526
|
+
import { usePathname, useRouter } from 'next/navigation';
|
|
1527
|
+
const pathname = usePathname();
|
|
1528
|
+
const router = useRouter();
|
|
1529
|
+
|
|
1530
|
+
// Translation (all text)
|
|
1531
|
+
const t = useTranslations('namespace');
|
|
1532
|
+
<h1>{t('title')}</h1>
|
|
1533
|
+
|
|
1534
|
+
// Components (use HeroUI, not HTML)
|
|
1535
|
+
import { Button } from '@/components/ui';
|
|
1536
|
+
<Button color="primary">Click</Button> // ✅
|
|
1537
|
+
<button>Click</button> // ❌
|
|
1538
|
+
|
|
1539
|
+
// Data mapping (from constants)
|
|
1540
|
+
const menuItems = [
|
|
1541
|
+
{ key: 'home', labelKey: 'home', href: NAVIGATION_URLS.ROOT },
|
|
1542
|
+
];
|
|
1543
|
+
{menuItems.map(item => <Item key={item.key}>{t(item.labelKey)}</Item>)}
|
|
1544
|
+
|
|
1545
|
+
// Responsive (mobile-first)
|
|
1546
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
|
|
1171
1547
|
```
|
|
1172
1548
|
|
|
1173
1549
|
---
|
|
1174
1550
|
|
|
1175
|
-
**Remember**: This project has a well-defined structure
|
|
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.
|
|
1551
|
+
**Remember**: This project has a well-defined structure and component library. Your job is to use them correctly, not to reinvent them. Stay within the boundaries of the existing architecture.
|