Components UI
Giới thiệu
Dự án sử dụng một hệ thống components UI đã được tối ưu và tùy chỉnh. Các components được thiết kế để dễ sử dụng, linh hoạt và nhất quán trên toàn bộ ứng dụng.
OptimizedImage
OptimizedImage là một wrapper cho next/image với các tính năng bổ sung như xử lý lỗi, tùy chọn hình ảnh và hỗ trợ tốt hơn cho lazy loading.
Cách sử dụng
1import { OptimizedImage } from "@/components/ui/optimized-image"; 2 3// Cơ bản 4<OptimizedImage 5 src="/path/to/image.jpg" 6 alt="Mô tả hình ảnh" 7 width={800} 8 height={600} 9/> 10 11// Fill container 12<div className="relative w-full h-64"> 13 <OptimizedImage 14 src="/path/to/image.jpg" 15 alt="Mô tả hình ảnh" 16 fill 17 priority 18 /> 19</div> 20 21// Với objectFit 22<OptimizedImage 23 src="/path/to/image.jpg" 24 alt="Mô tả hình ảnh" 25 fill 26 objectFit="contain" 27/>
Props
| Prop | Type | Mặc định | Mô tả |
|---|---|---|---|
| src | string | (bắt buộc) | Đường dẫn đến hình ảnh |
| alt | string | (bắt buộc) | Mô tả hình ảnh cho accessibility |
| width | number | undefined | Chiều rộng của hình ảnh |
| height | number | undefined | Chiều cao của hình ảnh |
| priority | boolean | false | Tải hình ảnh với mức ưu tiên cao hơn |
| className | string | undefined | Class CSS cho component |
| sizes | string | "100vw" | Kích thước responsive cho hình ảnh |
| fill | boolean | false | Làm đầy container cha |
| objectFit | "cover" | "contain" | "fill" | "none" | "scale-down" | "cover" | Cách hình ảnh được hiển thị trong container |
| quality | number | 80 | Chất lượng hình ảnh |
| placeholder | "blur" | "empty" | undefined | Loại placeholder |
| blurDataURL | string | undefined | URL cho blurred placeholder |
Layout Components
Container
1<div className="container mx-auto px-4"> 2 Nội dung 3</div>
Grid Layout
1<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> 2 <div>Item 1</div> 3 <div>Item 2</div> 4 <div>Item 3</div> 5</div>
Flex Layout
1<div className="flex flex-col md:flex-row items-center justify-between"> 2 <div>Item bên trái</div> 3 <div>Item bên phải</div> 4</div>
Text Components
Headings
1<h1 className="text-4xl font-bold mb-6">Tiêu đề chính</h1> 2<h2 className="text-3xl font-bold mb-4">Tiêu đề phụ</h2> 3<h3 className="text-2xl font-bold mb-3">Tiêu đề cấp 3</h3> 4<h4 className="text-xl font-bold mb-2">Tiêu đề cấp 4</h4>
Paragraphs
1<p className="text-base text-gray-700 mb-4"> 2 Đoạn văn bản thông thường. 3</p> 4 5<p className="text-sm text-gray-500"> 6 Đoạn văn bản nhỏ hơn, thường dùng cho chú thích. 7</p>
Interactive Components
Buttons
1// Primary Button 2<button className="bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-lg transition duration-300"> 3 Nút chính 4</button> 5 6// Secondary Button 7<button className="border border-blue-600 text-blue-600 hover:bg-blue-600 hover:text-white font-medium py-2 px-4 rounded-lg transition duration-300"> 8 Nút phụ 9</button> 10 11// Ghost Button 12<button className="text-blue-600 hover:text-blue-700 font-medium transition duration-300"> 13 Nút văn bản 14</button>
Form Controls
1// Input 2<input 3 type="text" 4 className="w-full border border-gray-300 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" 5 placeholder="Nhập thông tin..." 6/> 7 8// Textarea 9<textarea 10 className="w-full border border-gray-300 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" 11 rows={4} 12 placeholder="Nhập nội dung..." 13/> 14 15// Select 16<select className="w-full border border-gray-300 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"> 17 <option value="">Chọn một mục</option> 18 <option value="option1">Tùy chọn 1</option> 19 <option value="option2">Tùy chọn 2</option> 20</select> 21 22// Checkbox 23<div className="flex items-center"> 24 <input 25 type="checkbox" 26 id="checkbox" 27 className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" 28 /> 29 <label htmlFor="checkbox" className="ml-2 text-gray-700"> 30 Tùy chọn 31 </label> 32</div>
Cards
Blog Card
1import { OptimizedImage } from "@/components/ui/optimized-image"; 2import Link from "next/link"; 3 4function BlogCard({ post, locale }) { 5 return ( 6 <div className="bg-white rounded-lg shadow-md overflow-hidden"> 7 {post.cover?.absolute_url && ( 8 <div className="relative aspect-video overflow-hidden"> 9 <OptimizedImage 10 src={post.cover.absolute_url} 11 alt={post.title} 12 fill 13 className="transition-transform hover:scale-105 duration-300" 14 /> 15 </div> 16 )} 17 18 <div className="p-5"> 19 <h3 className="text-xl font-semibold mb-2"> 20 <Link href={`/${locale}/blog/${post.slug}`} className="hover:text-blue-600"> 21 {post.title} 22 </Link> 23 </h3> 24 25 {post.description && ( 26 <p className="text-gray-600 mb-4 line-clamp-2"> 27 {post.description} 28 </p> 29 )} 30 31 <div className="text-sm text-gray-500"> 32 {new Date(post.created_at).toLocaleDateString()} 33 </div> 34 </div> 35 </div> 36 ); 37}
Sử dụng biến Tailwind
Dự án sử dụng TailwindCSS với một số biến tùy chỉnh. Bạn có thể tham khảo file tailwind.config.ts để xem danh sách đầy đủ các biến.
1// Sử dụng màu theo brand 2<div className="bg-primary text-white"> 3 Nội dung với màu chính 4</div> 5 6// Sử dụng font sizes 7<p className="text-base md:text-lg"> 8 Văn bản responsive 9</p> 10 11// Sử dụng spacing 12<div className="p-4 md:p-6 lg:p-8"> 13 Nội dung với spacing khác nhau 14</div>
Responsive Design
Tất cả các components đều được thiết kế để responsive và hoạt động tốt trên tất cả các kích thước màn hình.
1<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4"> 2 {/* Các items sẽ hiển thị 1, 2, 3 hoặc 4 cột tùy thuộc vào kích thước màn hình */} 3</div>
Khuyến nghị và thực tiễn tốt nhất
- Sử dụng OptimizedImage: Luôn sử dụng
OptimizedImagethay vìImagetừ next/image trực tiếp - Tailwind Classes: Tổ chức các classes Tailwind một cách logic và nhất quán
- Responsive: Luôn thiết kế cho mobile-first, sau đó mở rộng cho các màn hình lớn hơn
- Typography: Tuân thủ hệ thống typography đã định nghĩa
- Tối ưu hóa: Sử dụng
prioritycho LCP (Largest Contentful Paint) images - A11y: Đảm bảo tất cả các images có thuộc tính
altphù hợp
