# Component Architecture β€” The Glass Library > *"Design is not just what it looks like and feels like. Design is how it works."* > β€” Steve Jobs --- ## 🎯 Filosofi Komponen Setiap komponen di BETE adalah **self-contained glass panel** yang: 1. **Satu tanggung jawab** β€” Satu komponen, satu fungsi 2. **State-driven** β€” Visual merepresentasikan state, bukan sebaliknya 3. **Composable** β€” Bisa digabung seperti LEGO 4. **Theme-aware** β€” Menggunakan CSS variables, bukan hardcoded values 5. **Accessible** β€” Keyboard, screen reader, reduced motion --- ## πŸ“ Component Taxonomy ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ COMPONENT MAP β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€ ATOMS ──────────────────────────────────────────┐ β”‚ β”‚ β”‚ Button β”‚ Badge β”‚ Input β”‚ Label β”‚ Icon β”‚ β”‚ β”‚ β”‚ Avatar β”‚ Skeletonβ”‚ Spinner β”‚ Dividerβ”‚ Tooltip β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ ↓ β”‚ β”‚ β”Œβ”€β”€β”€ MOLECULES ───────────────────────────────────────┐ β”‚ β”‚ β”‚ Card β”‚ Tabs β”‚ Select β”‚ β”‚ β”‚ β”‚ Toast β”‚ Modal β”‚ Dropdown β”‚ β”‚ β”‚ β”‚ Pagination β”‚ Breadcrumb β”‚ SearchBar β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ ↓ β”‚ β”‚ β”Œβ”€β”€β”€ ORGANISMS ───────────────────────────────────────┐ β”‚ β”‚ β”‚ MessageCard β”‚ VoiceCard β”‚ AnalyticsChart β”‚ β”‚ β”‚ β”‚ ActiveSpeaker β”‚ NowPlaying β”‚ ImageGrid β”‚ β”‚ β”‚ β”‚ MascotChatbot β”‚ AudioViz β”‚ StatsCard β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ ↓ β”‚ β”‚ β”Œβ”€β”€β”€ TEMPLATES ───────────────────────────────────────┐ β”‚ β”‚ β”‚ Sidebar β”‚ Header β”‚ DashboardLayout β”‚ β”‚ β”‚ β”‚ AuthForm β”‚ LivePanel β”‚ MessagesPanel β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` --- ## 🧱 Atomic Components (Atoms) ### Button ```tsx interface ButtonProps { variant: 'primary' | 'secondary' | 'destructive' | 'outline' | 'ghost'; size: 'sm' | 'default' | 'lg' | 'icon'; loading?: boolean; disabled?: boolean; icon?: ReactNode; children?: ReactNode; } ``` ```css .button { --btn-bg: var(--clr-primary); --btn-color: var(--clr-text-on-primary); --btn-border: transparent; --btn-hover-bg: var(--clr-primary-600); --btn-active-transform: scale(0.97); display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-1); border-radius: var(--rd-md); font-family: var(--ff-sans); font-size: var(--fs-sm); font-weight: var(--fw-medium); line-height: var(--lh-compact); white-space: nowrap; cursor: pointer; background: var(--btn-bg); color: var(--btn-color); border: 1px solid var(--btn-border); transition: transform var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out); padding: var(--sp-1) var(--sp-3); height: 40px; } .button--sm { height: 32px; padding: var(--sp-0-5) var(--sp-2); } .button--lg { height: 48px; padding: var(--sp-2) var(--sp-4); } .button--icon { height: 40px; width: 40px; padding: 0; } .button--secondary { --btn-bg: var(--clr-interactive-hover); --btn-color: var(--clr-text); } .button--destructive { --btn-bg: var(--clr-ruby-500); --btn-color: white; } .button--outline { --btn-bg: transparent; --btn-color: var(--clr-text); --btn-border: var(--clr-border); } .button--ghost { --btn-bg: transparent; --btn-color: var(--clr-text); } .button:hover { background: var(--btn-hover-bg); } .button:active { transform: var(--btn-active-transform); } .button:disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; } .button:focus-visible { outline: 2px solid var(--clr-primary); outline-offset: 2px; } ``` ### Badge ```tsx interface BadgeProps { variant: 'default' | 'secondary' | 'success' | 'warning' | 'destructive' | 'outline' | 'severity'; severity?: 'safe' | 'low' | 'medium' | 'high' | 'critical'; size?: 'sm' | 'default'; dot?: boolean; children: ReactNode; } ``` --- ## ⛓️ Molecular Components (Molecules) ### Card ```tsx interface CardProps { variant?: 'default' | 'elevated' | 'glass' | 'interactive'; padding?: 'sm' | 'default' | 'lg' | 'none'; hover?: boolean; as?: 'div' | 'button' | 'a'; onClick?: () => void; children: ReactNode; } // Sub-components Card.Header β€” flex-col gap-1.5 Card.Title β€” h3, font-semibold Card.Description β€” p, text-sm, text-muted Card.Content β€” main area with padding Card.Footer β€” flex items-center ``` ### Modal / Dialog ```tsx interface ModalProps { open: boolean; onClose: () => void; title?: string; description?: string; size?: 'sm' | 'default' | 'lg' | 'full'; closeOnOverlay?: boolean; children: ReactNode; } ``` ### Toast ```tsx interface Toast { id: string; type: 'info' | 'success' | 'error' | 'warning'; title: string; description?: string; duration?: number; // Auto-dismiss ms, 0 = persistent action?: { label: string; onClick: () => void; }; } function useToast(): { toast: (t: Omit) => string; dismiss: (id: string) => void; toasts: Toast[]; } ``` --- ## 🧬 Organism Components ### MessageCard ```tsx interface MessageCardProps { message: { id: string; content: string; author: { id: string; name: string; avatar: string; }; timestamp: number; channel?: { id: string; name: string; }; attachments?: Attachment[]; aiAnalysis?: { status: 'pending' | 'analyzing' | 'complete' | 'error'; severity?: 'safe' | 'low' | 'medium' | 'high' | 'critical'; categories?: string[]; summary?: string; }; isEdited?: boolean; isDeleted?: boolean; }; onReanalyze?: (messageId: string) => Promise; } ``` ### AudioVisualizer β€” Canvas-based ```tsx interface AudioVisualizerProps { frequencies: Uint8Array; barCount?: number; // Default 48 gradient?: [string, string]; height?: number; // Default 32 mirrored?: boolean; } ``` --- ## 🎯 State Management per Komponen Setiap komponen mengelola state visual: ```tsx // 1. Normal
...
// 2. Hover
...
// 3. Active/Focus
...
// 4. Disabled
...
// Β± Loading (data-dependent)
// Β± Error (data-dependent)

Failed to load

// Β± Empty (data-dependent)
``` --- ## ⚠️ Component Anti-Patterns ### ❌ Prop Drilling Berlebihan ```tsx // ❌ JANGAN β€” props turun 4 level // βœ… Gunakan context atau komposisi ``` ### ❌ Komponen terlalu besar ```tsx // ❌ JANGAN β€” 400+ baris function LivePanel() { /* 400 lines */ } // βœ… Bagi ke sub-komponen function LivePanel() { return (
); } ``` ### ❌ Conditional terlalu kompleks ```tsx // ❌ JANGAN β€” ternary bersarang return isError ? : isLoading ? : isEmpty ? : ; // βœ… State machine pattern const state = getComponentState({ isLoading, isError, isEmpty, data }); return ; ``` --- ## πŸ”— Referensi | Sumber | Konsep | |--------|--------| | [Atomic Design (Brad Frost)](https://atomicdesign.bradfrost.com/) | Atom-molecule-organism | | [Radix UI](https://www.radix-ui.com/) | Headless UI primitives | | [shadcn/ui](https://ui.shadcn.com/) | Component pattern reference | --- *"Komponen adalah kristal ingatan β€” setiap bagian kecil menyatu membentuk keindahan yang utuh."* β„οΈπŸ©΅