15 KiB
15 KiB
ADR-012: UI Component Library Strategy
Status: ✅ Accepted Date: 2026-02-24 Decision Makers: Frontend Team, UX Designer Related Documents: Frontend Guidelines, ADR-005: Technology Stack Version Applicability: v1.8.0+ Next Review: 2026-08-01 (6-month cycle)
Gap Analysis & Requirement Linking
ปิด Gap จาก Requirements:
| Gap/Requirement | แหล่งที่มา | วิธีการแก้ไขใน ADR นี้ |
|---|---|---|
| Design Consistency | Product Vision - UI/UX Requirements | Shadcn/UI for consistent design system |
| Accessibility Support | Acceptance Criteria - AC-UI-003 | Radix UI primitives for WCAG 2.1 AA |
| Performance Optimization | Frontend Guidelines - Performance | Tree-shakeable components, minimal bundle |
| Customization Flexibility | Engineering Guidelines - DX | Full ownership of component code |
| Bundle Size Constraints | Architecture - Performance | Copy-only-what-you-use approach |
แก้ไขความขัดแย้ง:
- Conflict: Library vs. Custom (MUI/Ant Design vs. Shadcn/UI)
- Resolution: Chose Shadcn/UI for full control and minimal bundle
- Trade-off: Manual updates vs. Complete customization freedom
Impact Analysis
Affected Components (ส่วนประกอบที่ได้รับผลกระทบ):
| Component | ผลกระทบ | ความสำคัญ |
|---|---|---|
| UI Components | All UI components use Shadcn/UI | 🔴 Critical |
| Tailwind Config | CSS variables and theming | 🔴 Critical |
| Component Library | Custom component compositions | 🔴 Critical |
| Form Components | Integration with React Hook Form | 🟡 Important |
| Layout Components | Page layouts and navigation | 🟡 Important |
| Theme System | Dark/light mode support | 🟡 Important |
| Component Testing | Unit tests for custom components | 🟡 Important |
| Documentation | Component usage guidelines | 🟢 Guidelines |
| Update Process | Manual component update workflow | 🟢 Guidelines |
Required Changes (การเปลี่ยนแปลงที่ต้องดำเนินการ):
Frontend (Next.js)
- Initialize Shadcn/UI project
- Add core components (Button, Input, Card, etc.)
- Setup Tailwind CSS with CSS variables
- Create custom component compositions
- Implement theme switching
- Update all pages to use new components
- Add component testing
Design System
- Define design tokens and colors
- Create component usage guidelines
- Document customization patterns
- Setup component update workflow
Context and Problem Statement
ต้องการ UI Component Library สำหรับสร้าง User Interface ที่สวยงาม สม่ำเสมอ และ Accessible
ปัญหาที่ต้องแก้:
- Component Library: ใช้ Library สำเร็จรูป หรือสร้างเอง
- Customization: ปรับแต่งได้ง่ายเพียงใด
- Accessibility: รองรับ ARIA และ Keyboard navigation
- Bundle Size: ขนาดไฟล์ไม่ใหญ่เกินไป
- Developer Experience: ใช้งานง่าย Documentation ครบ
Decision Drivers
- 🎨 Design Consistency: UI สม่ำเสมอทั้งระบบ
- ♿ Accessibility: รองรับ WCAG 2.1 AA
- 🎯 Customization: ปรับแต่งได้ตามต้องการ
- 📦 Bundle Size: เล็กและ Tree-shakeable
- ⚡ Performance: Render เร็ว
- 🛠️ DX: Developer Experience ดี
Considered Options
Option 1: Material-UI (MUI)
Pros:
- ✅ Component ครบชุด
- ✅ Documentation ดี
- ✅ Community ใหญ่
- ✅ Built-in theming
Cons:
- ❌ Bundle size ใหญ่
- ❌ Design opinionated (Material Design)
- ❌ Customization ยาก
- ❌ Performance overhead
Option 2: Ant Design
Pros:
- ✅ Component ครบ (เน้น Enterprise)
- ✅ i18n support ดี
- ✅ Form components ครบ
Cons:
- ❌ Bundle size ใหญ่มาก
- ❌ Chinese-centric design
- ❌ Customization จำกัด
- ❌ TypeScript support ไม่ดีเท่าไร
Option 3: Chakra UI
Pros:
- ✅ Accessibility ดี
- ✅ Customization ง่าย
- ✅ TypeScript first
- ✅ Dark mode built-in
Cons:
- ❌ Bundle size ค่อนข้างใหญ่
- ❌ CSS-in-JS overhead
- ❌ Performance issues with many components
Option 4: Headless UI + Tailwind CSS
Pros:
- ✅ Full control over styling
- ✅ Lightweight
- ✅ Accessibility ดี
- ✅ No styling overhead
Cons:
- ❌ ต้องเขียน styles เอง
- ❌ Component library น้อย
- ❌ ใช้เวลาพัฒนานาน
Option 5: Shadcn/UI + Tailwind CSS
วิธีการ: Copy components ที่ต้องการไปยัง Project
Pros:
- ✅ Full ownership: Components เป็นของเรา ไม่ใช่ dependency
- ✅ Highly customizable: แก้ไขได้เต็มที่
- ✅ Accessibility: ใช้ Radix UI Primitives
- ✅ Bundle size: เฉพาะที่ใช้เท่านั้น
- ✅ Tailwind CSS: Utility-first ง่ายต่อการ maintain
- ✅ TypeScript: Type-safe
- ✅ Beautiful defaults: Design ดูทันสมัย
Cons:
- ❌ ต้อง Copy components เอง
- ❌ Update ต้องทำด้วยตัวเอง
- ❌ ไม่มี
npm installแบบ Library
Decision Outcome
Chosen Option: Option 5 - Shadcn/UI + Tailwind CSS
Rationale
- Ownership: เป็นเจ้าของ Code 100% ปรับแต่งได้อย่างเต็มที่
- Bundle Size: เล็กที่สุด (เฉพาะที่ใช้)
- Accessibility: ใช้ Radix UI primitives ที่ทดสอบแล้ว
- Customization: แก้ไขได้ตามต้องการ ไม่ติด Framework
- Tailwind CSS: ทีมคุ้นเคยและใช้อยู่แล้ว
- Modern Design: ดูสวยงามและทันสมัย
Implementation Details
1. Setup Shadcn/UI
# Initialize shadcn/ui
npx shadcn-ui@latest init
# Select options:
# - TypeScript: Yes
# - Style: Default
# - Base color: Slate
# - CSS variables: Yes
// File: components.json
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "app/globals.css",
"baseColor": "slate",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}
2. Add Components
# Add specific components
npx shadcn-ui@latest add button
npx shadcn-ui@latest add input
npx shadcn-ui@latest add card
npx shadcn-ui@latest add dialog
npx shadcn-ui@latest add dropdown-menu
npx shadcn-ui@latest add table
# Components will be copied to components/ui/
3. Component Usage
// File: app/correspondences/page.tsx
import { Button } from '@/components/ui/button';
import { Card } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
export default function CorrespondencesPage() {
return (
<div className="space-y-4">
<div className="flex justify-between items-center">
<Input placeholder="Search..." className="max-w-sm" />
<Button>Create New</Button>
</div>
<Card className="p-6">
<h2 className="text-xl font-bold">Correspondences</h2>
{/* Content */}
</Card>
</div>
);
}
4. Customize Components
// File: components/ui/button.tsx
// สามารถแก้ไขได้เต็มที่เพราะเป็น Code ของเรา
import * as React from 'react';
import { Slot } from '@radix-ui/react-slot';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive:
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline: 'border border-input bg-background hover:bg-accent',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
// Add custom variant
success: 'bg-green-600 text-white hover:bg-green-700',
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : 'button';
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
);
}
);
Button.displayName = 'Button';
export { Button, buttonVariants };
5. Theming with CSS Variables
/* File: app/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--primary: 221.2 83.2% 53.3%;
--primary-foreground: 210 40% 98%;
/* ... more colors */
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
/* ... dark mode colors */
}
}
6. Component Composition
// File: components/correspondence/card.tsx
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
export function CorrespondenceCard({ correspondence }) {
return (
<Card>
<CardHeader>
<div className="flex justify-between items-start">
<CardTitle>{correspondence.subject}</CardTitle>
<Badge
variant={
correspondence.status === 'APPROVED' ? 'success' : 'default'
}
>
{correspondence.status}
</Badge>
</div>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground">
{correspondence.description}
</p>
<div className="mt-4 flex gap-2">
<Button variant="outline" size="sm">
View
</Button>
<Button size="sm">Edit</Button>
</div>
</CardContent>
</Card>
);
}
Component Inventory
Core Components (มีอยู่ใน Shadcn/UI)
Forms:
- Button
- Input
- Textarea
- Select
- Checkbox
- Radio Group
- Switch
- Slider
- Label
Data Display:
- Table
- Card
- Badge
- Avatar
- Separator
Feedback:
- Alert
- Dialog
- Toast
- Progress
- Skeleton
Navigation:
- Tabs
- Dropdown Menu
- Command
- Popover
- Sheet (Drawer)
Layout:
- Accordion
- Collapsible
- Aspect Ratio
- Scroll Area
Consequences
Positive Consequences
- ✅ Full Control: แก้ไข Components ได้เต็มที่
- ✅ Smaller Bundle: เฉพาะที่ใช้เท่านั้น
- ✅ No Lock-in: ไม่ติด Dependency
- ✅ Accessibility: ใช้ Radix UI (tested)
- ✅ Beautiful Design: ดูทันสมัยและสวยงาม
- ✅ TypeScript: Type-safe
Negative Consequences
- ❌ Manual Updates: ต้อง Update components ด้วยตัวเอง
- ❌ Initial Setup: ต้อง Copy components ที่ต้องการ
- ❌ No Official Support: ไม่มี Package maintainer
Mitigation Strategies
- Documentation: เขียนเอกสารว่า Components ไหนมา version ไหน
- Changelog: Track changes ที่ทำกับ Components
- Testing: เขียน Tests สำหรับ Custom components
- Review Updates: Check Shadcn/UI releases เป็นระยะ (แนะนำให้ใช้
npx shadcn-ui@latest diffตรวจสอบความแตกต่างทุกๆ X เดือนเพื่อลดภาระการอัปเดตแบบ manual)
ADR Review Cycle
Core Principle Review Schedule
- Review Frequency: ทุก 6 เดือน (กุมภาพันธ์ และ สิงหาคม)
- Trigger Events:
- Major version upgrade (v1.9.0, v2.0.0)
- Shadcn/UI major updates
- New component requirements
- Accessibility standard updates
Review Checklist
- Component library still meets design requirements
- Bundle size within acceptable limits
- Accessibility compliance maintained
- Custom components properly documented
- Cross-document dependencies still valid
- New component libraries to consider
- Component update workflow effective
Version Dependency Matrix
| System Version | ADR Version | Required Changes | Status |
|---|---|---|---|
| v1.8.0 - v1.8.5 | ADR-012 v1.0 | Base Shadcn/UI implementation | ✅ Complete |
| v1.9.0+ | ADR-012 v1.1 | Review component updates and performance | 📋 Planned |
| v2.0.0+ | ADR-012 v2.0 | Consider new UI patterns or libraries | 📋 Future |
Related ADRs
- ADR-005: Technology Stack - เลือกใช้ Tailwind CSS
- ADR-011: Next.js App Router
References
Document Version: v1.0 Last Updated: 2026-02-24 Next Review: 2026-08-01 (6-month cycle) Version Applicability: LCBP3 v1.8.0+
Change History
| Version | Date | Changes | Author |
|---|---|---|---|
| v1.0 | 2026-02-24 | Initial ADR creation with UI component strategy | Frontend Team |
| v1.1 | 2026-04-04 | Added structured templates: Impact Analysis, Gap Linking, Version Dependency, Review Cycle | System Architect |