🧩 Component Specification
Target: Figma master components for Amal E-Santri Design System Used by: Designer (untuk build components) + Flutter Developer (nanti saat implement) Total components: 12 atoms + molecules + organisms
🧬 ATOMS
1. Button
Purpose: Primary action trigger dengan varian semantic dan size.
Component Properties:
| Property | Type | Values | Default |
|---|---|---|---|
variant | Variant | primary, secondary, tertiary, danger, ghost | primary |
size | Variant | xs, sm, md, lg | md |
state | Variant | default, hover, pressed, loading, disabled | default |
icon | Variant | none, leading, trailing, only | none |
width | Variant | hug, fill | hug |
label | Text | — | "Button" |
iconName | Instance swap | Any icon | — |
Sizing spec:
| Size | Height | Padding H | Padding V | Gap | Font | Icon |
|---|---|---|---|---|---|---|
xs | 28px | 12px | 4px | 4px | body-sm 500 | 14px |
sm | 36px | 16px | 8px | 6px | body-sm 600 | 16px |
md | 44px | 20px | 12px | 8px | body 600 | 18px |
lg | 52px | 24px | 14px | 10px | body-lg 600 | 20px |
Style per variant (Light mode):
| Variant | BG | Text | Border | Shadow |
|---|---|---|---|---|
primary | brand/primary (emerald-600) | text/on-primary (white) | none | shadow/sm |
secondary | surface/card (white) | brand/primary | border/default | none |
tertiary | transparent | brand/primary | none | none |
danger | feedback/error (red-500) | white | none | shadow/sm |
ghost | surface/variant (zinc-100) | text/primary | none | none |
State style modifiers:
hover: lighten BG 5%, add subtle shadowpressed: darken BG 10%, remove shadowloading: show spinner, disable click, opacity 0.8disabled: opacity 0.4, cursor not-allowed, no hover effect
Border radius: radius/md (12px) for all sizes
Usage do's:
- ✅ Use
primaryuntuk ONE primary action per screen - ✅ Use
secondaryuntuk alternative action (e.g. "Cancel", "Lihat Detail") - ✅ Use
tertiaryuntuk inline text-style action - ✅ Use
dangeruntuk destructive (delete, keluar) - ✅ Use
ghostuntuk tertiary action dalam form/list
Usage don'ts:
- ❌ Jangan pakai 2 primary buttons berdampingan
- ❌ Jangan pakai
xsdi primary CTA - ❌ Jangan override color manually — pakai variant
2. Chip
Purpose: Compact label untuk status, category, atau filter.
Component Properties:
| Property | Type | Values | Default |
|---|---|---|---|
semantic | Variant | hadir, izin, sakit, alpha, terlambat, lunas, belum-bayar, overdue, neutral | neutral |
style | Variant | tonal, filled, outlined | tonal |
size | Variant | sm, md | sm |
hasIcon | Boolean | true/false | false |
label | Text | — | "Chip" |
Sizing spec:
| Size | Height | Padding H | Padding V | Gap | Font |
|---|---|---|---|---|---|
sm | 20px | 8px | 2px | 4px | 11px 600 |
md | 24px | 12px | 4px | 4px | 12px 600 |
Style per semantic (tonal variant):
Semua pattern: bg: {semantic}-bg, text: {semantic}, border: none
| Semantic | BG | Text |
|---|---|---|
hadir | status/hadir-bg (emerald-50) | status/hadir (emerald-700) |
izin | status/izin-bg (amber-50) | status/izin (amber-700) |
sakit | status/sakit-bg (blue-50) | status/sakit (blue-700) |
alpha | status/alpha-bg (red-50) | status/alpha (red-700) |
terlambat | status/terlambat-bg (orange-50) | status/terlambat (orange-700) |
lunas | status/lunas-bg (emerald-50) | status/lunas (emerald-700) |
belum-bayar | status/belum-bayar-bg (amber-50) | status/belum-bayar (amber-700) |
overdue | status/overdue-bg (red-50) | status/overdue (red-700) |
neutral | surface/variant (zinc-100) | text/secondary |
Border radius: radius/sm (8px) atau radius/full untuk pill-style
3. Avatar
Purpose: Representasi user/student dengan foto atau inisial.
Component Properties:
| Property | Type | Values | Default |
|---|---|---|---|
size | Variant | xs, sm, md, lg, xl | md |
type | Variant | image, initials, glass | initials |
badge | Variant | none, dot, number | none |
border | Variant | none, solid, glass | none |
initials | Text | — | "AF" |
imageSrc | Image fill | — | — |
badgeContent | Text | — | "1" |
Sizing spec:
| Size | Diameter | Font (initials) | Badge offset |
|---|---|---|---|
xs | 24px | 10px 600 | top-right 2px |
sm | 32px | 12px 700 | top-right 2px |
md | 40px | 14px 700 | top-right 2px |
lg | 56px | 18px 700 | top-right 4px |
xl | 80px | 24px 700 | top-right 6px |
Glass variant (digunakan di hero card di atas gradient):
- BG:
rgba(255, 255, 255, 0.18) - Border:
1px solid rgba(255, 255, 255, 0.3) - Backdrop blur: 10px
- Text: white
Color generation logic (for initials): Pakai deterministic hash dari nama → pick dari palette:
emerald-100 / emerald-700amber-100 / amber-700blue-100 / blue-700pink-100 / pink-700purple-100 / purple-700
4. Icon
Purpose: Standardized icon wrapper dengan token-based sizing.
Component Properties:
| Property | Type | Values | Default |
|---|---|---|---|
size | Variant | sm (16), md (20), lg (24), xl (32) | md |
color | Token ref | Any color token | text/primary |
iconName | Instance swap | Material Symbol | — |
Rules:
- Library: Material Symbols Rounded (recommended — modern + free) atau Phosphor
- Stroke width konsisten: 2px
- Color HARUS reference token (tidak hardcode)
🔬 MOLECULES
5. Card
Purpose: Container untuk konten grouped.
Component Properties:
| Property | Type | Values | Default |
|---|---|---|---|
variant | Variant | flat, elevated, outlined | flat |
radius | Variant | md, lg, xl | lg |
padding | Variant | sm (12), md (16), lg (20) | md |
hasAction | Boolean | — | false |
interactive | Boolean | — | false |
Style per variant:
| Variant | BG | Border | Shadow |
|---|---|---|---|
flat | surface/card | none | none |
elevated | surface/card | none | shadow/card |
outlined | surface/card | border/default 1px | none |
Interactive state:
- Add
hovershadow pada elevated - Add
pressedscale 0.98 feedback
6. TextField
Purpose: Form input dengan 7 states.
Component Properties:
| Property | Type | Values | Default |
|---|---|---|---|
state | Variant | empty, focused, filled, error, disabled, success, loading | empty |
size | Variant | sm, md, lg | md |
hasLabel | Boolean | — | true |
hasLeadingIcon | Boolean | — | false |
hasTrailingIcon | Boolean | — | false |
hasHelperText | Boolean | — | false |
hasCharCount | Boolean | — | false |
label | Text | — | "Label" |
value | Text | — | "" |
placeholder | Text | — | "Placeholder" |
helperText | Text | — | "Helper" |
Sizing spec:
| Size | Height | Padding | Font |
|---|---|---|---|
sm | 36px | 12px | body-sm |
md | 48px | 14px | body |
lg | 56px | 16px | body-lg |
State colors:
| State | Border | Label | Text | BG |
|---|---|---|---|---|
empty | border/default | text/secondary | — | surface/card |
focused | border/focus 2px | border/focus | text/primary | surface/card |
filled | border/default | text/secondary | text/primary | surface/card |
error | feedback/error | feedback/error-text | text/primary | surface/card |
disabled | border/default | text/disabled | text/disabled | surface/variant |
success | feedback/success | feedback/success-text | text/primary | surface/card |
loading | border/default | text/secondary | text/primary | surface/card (shimmer overlay) |
Border radius: radius/md (12px)
7. AppBar
Purpose: Top navigation bar per screen.
Component Properties:
| Property | Type | Values | Default |
|---|---|---|---|
variant | Variant | default, with-back, with-action, large-title | default |
title | Text | — | "Title" |
hasSubtitle | Boolean | — | false |
subtitle | Text | — | "Subtitle" |
trailingActionCount | Variant | 0, 1, 2 | 0 |
Layout spec:
- Height: 56px (default), 96px (large-title)
- BG:
surface/appbar - Shadow:
0 1px 4px rgba(0,0,0,0.04)(on scroll only) - Padding: 16px horizontal, 0 vertical (content centered vertically)
Status bar: match ke light/dark mode context.
8. BottomNav
Purpose: Persona-aware bottom navigation.
Component Properties:
| Property | Type | Values | Default |
|---|---|---|---|
persona | Variant | parent, guru, walikelas, bendahara, admin, kepsek | parent |
activeTab | Variant | depends on persona | — |
badgeOnTab | Variant | none, tab1, tab2, tab3, tab4, tab5, tab6 | none |
Layout per persona:
Parent (6 tabs):
| Tab | Icon | Label | Route |
|---|---|---|---|
| 1 | home | Beranda | parent.dashboard |
| 2 | receipt-text | Tagihan | parent.billing |
| 3 | piggy-bank | Tabungan | parent.savings |
| 4 | school | Nilai | parent.grades |
| 5 | calendar-check | Absensi | parent.attendance |
| 6 | account | Profil | parent.profile |
Sizing:
- Height: 68px (including safe area)
- Bg:
surface/bottomtab - Top border/shadow:
shadow/bottomtab - Tab min-width:
100% / tab-count - Icon size: 22px
- Label: 10-11px semibold
- Active indicator: 28×3px pill di atas icon, color
brand/primary
Safe area handling:
- Android: adjust bottom padding via
env(safe-area-inset-bottom)(CSS) atauMediaQuery.viewPadding.bottom(Flutter) - iOS: auto-handled via SafeArea widget
🧱 ORGANISMS
9. HeroCard (untuk Dashboard)
Purpose: Greeting + context hero block di top dashboard.
Component Properties:
| Property | Type | Values | Default |
|---|---|---|---|
persona | Variant | parent, guru, walikelas | parent |
hasChildSwitcher | Boolean | — | true (parent) |
greeting | Text | — | "Assalamu'alaikum" |
name | Text | — | "Fatimah" |
subtitle | Text | — | "Semoga hari Anda penuh keberkahan" |
childName | Text | — | "Ahmad Fauzan" |
childClass | Text | — | "Kelas VII-A" |
hijriDate | Text | — | "8 Syawal 1447" |
Layout spec:
- Width: screen - 32 (16px margin tiap sisi)
- Height: 168px
- Border radius:
radius/xl(20px) - Padding: 24px (all sides)
- BG gradient (parent):
linear-gradient(156deg, #057E6B 0%, #065F53 42%, #06443E 100%) - BG gradient (guru):
linear-gradient(156deg, #0D9488 0%, #0F766E 42%, #115E59 100%) - Shadow:
0 8px 24px rgba(5, 150, 105, 0.25)(colored, brand)
Internal structure (Auto Layout):
HeroCard (VStack, gap 0)
├── Decor Layer (absolute, z-index below content)
│ ├── Rotated square 1 (top-right, 120×120, border alpha 0.08)
│ └── Rotated square 2 (top-right offset, 80×80, border alpha 0.08)
├── Top Row (VStack, gap 4)
│ ├── Greeting Label (micro-label, white/60)
│ ├── Name (display, bold, white)
│ └── Subtitle (body-sm, white/75)
├── Divider (1px, white/12, margin-y 16)
└── Bottom Row (HStack, space-between)
├── Child Switcher Card (glass, interactive)
│ ├── Avatar (glass xs, initials)
│ └── Info (VStack, gap 2)
│ ├── Label (micro-label, white/50)
│ └── Text (body-sm semibold, white)
└── Hijri Date Pill (glass, chip-like)
├── Icon (16px, white)
└── Text (micro-label, white)
10. QuickActionCard
Purpose: Grid item untuk quick action di Akses Cepat section.
Component Properties:
| Property | Type | Values | Default |
|---|---|---|---|
variant | Variant | default, active | default |
hasNotification | Boolean | — | false |
label | Text | — | "Tagihan" |
iconName | Instance swap | Any icon | — |
Sizing spec:
- Width:
(screen - 32 - 3*8) / 4≈ 88px (for 4-col grid with 8px gap, 16px side margin) - Height: 84px
- Border radius:
radius/md(14px — special 14px) - Padding: 12px
- Internal gap: 8px
Style per variant:
| Variant | BG | Border | Shadow |
|---|---|---|---|
default | surface/card | border/default | none |
active | surface/success-bg (emerald-50) | #86efac (emerald-300) | shadow/sm tinted emerald |
Notification dot:
- 5×5 circle,
feedback/error(red-500) - Position: top-right, offset 7px dari corner
11. EmptyState
Purpose: Consistent empty/error state pattern.
Component Properties:
| Property | Type | Values | Default |
|---|---|---|---|
variant | Variant | success, waiting, offline, error | waiting |
headline | Text | — | "Tidak Ada Data" |
subtext | Text | — | "Subtext description" |
hasPrimaryCTA | Boolean | — | false |
hasSecondaryCTA | Boolean | — | false |
primaryLabel | Text | — | "Coba Lagi" |
secondaryLabel | Text | — | "Hubungi Support" |
Icon & color per variant:
| Variant | Icon | Icon color | Circle BG | Use case |
|---|---|---|---|---|
success | check-circle atau receipt | feedback/success | feedback/success-bg | "Semua lunas — Alhamdulillah!" |
waiting | clock atau hourglass | feedback/info | feedback/info-bg | "Nilai belum di-input" |
offline | wifi-off | feedback/error | feedback/error-bg | "Tidak ada koneksi" |
error | alert-triangle | feedback/warning | feedback/warning-bg | "Server error" |
CTA behavior:
successvariant: tidak ada CTA (celebratory, terminal state)waitingvariant: tidak ada CTA (waiting on others) — opsional secondary "Hubungi Wali Kelas"offlinevariant: primary CTA "Coba Lagi" (filled)errorvariant: primary CTA "Coba Lagi" (filled) + secondary "Hubungi Support"
CTA style: SELALU primary filled (emerald-600). Jangan outline untuk retry action.
Layout spec:
EmptyState (VStack, centered, gap 16)
├── Circle icon wrapper (140×140, border-radius full)
│ └── Icon (64px)
├── Spacing (32px)
├── Headline (h2 semibold, text-primary, center)
├── Spacing (8px)
├── Subtext (body-sm, text-secondary, center, max 2 lines)
├── Spacing (24px)
├── Primary CTA (Button primary md, centered) [conditional]
├── Spacing (8px)
└── Secondary CTA (Button tertiary md, centered) [conditional]
12. SkeletonLoader
Purpose: Placeholder shimmer saat loading data.
Component Properties:
| Property | Type | Values | Default |
|---|---|---|---|
type | Variant | list-item, card, dashboard, detail | list-item |
Visual spec:
- Color:
surface/variant(zinc-100) base - Shimmer: linear-gradient animation, 1.5s loop
- Border radius: match actual content radius
Type variations:
list-item:
[Circle 40] [Rect 70% height 14] [Rect 50% height 12]
card:
[Rect 100% height 120]
[Rect 80% height 16]
[Rect 60% height 14]
dashboard:
[Hero rect 100% height 168]
[Grid 2x2 card: rect 100% height 100 × 4]
[List: list-item × 3]
detail:
[AppBar placeholder]
[Hero rect]
[Content blocks × 3]
🔄 COMPONENT DEPENDENCY GRAPH
Atoms Molecules Organisms
───── ───────── ─────────
Icon ─────┬─> Card ─────┬─> HeroCard
Button ────┤ TextField ────┤ QuickActionCard
Chip ─────┤ AppBar ────┤ EmptyState (uses Button)
Avatar ────┘ BottomNav ────┘ SkeletonLoader
(Future: BillCard, ScheduleWidget, etc)
Build order: Atoms → Molecules → Organisms. Never skip.
📖 Usage Pattern Examples
Example 1: Parent Dashboard
Screen Frame
├── AppBar (variant: default, hasAction: bell + avatar)
├── ScrollContent (VStack, gap 16, padding 16)
│ ├── HeroCard (persona: parent, hasChildSwitcher: true)
│ ├── AttendanceStrip (Card variant: outlined, emerald tint)
│ │ └── [Chip semantic: hadir] + Text
│ ├── Section "Akses Cepat"
│ │ └── Grid (4 columns, 8px gap)
│ │ └── QuickActionCard × 8 (2 dengan hasNotification)
│ ├── Section "Hari Ini"
│ │ └── LiveClassWidget (Card variant: elevated)
│ ├── HafalanWidget (Card)
│ └── ChatTeaser (Card)
└── BottomNav (persona: parent, activeTab: tab1)
Example 2: Empty Tagihan Screen
Screen Frame
├── AppBar (variant: default, title: "Tagihan")
├── EmptyState (variant: success, headline: "Tidak Ada Tagihan", subtext: "Semua tagihan sudah lunas. Alhamdulillah!")
└── BottomNav (persona: parent, activeTab: tab2)
✅ Validation Criteria
Component dianggap "done" jika:
- Semua Component Properties defined
- Semua variants ada (test dengan swap variants di Dev Mode)
- Semua values pakai tokens (0 hardcode)
- Auto Layout complete (no manual positioning)
- Dark mode works via theme switch
- Contrast check pass (use Stark plugin)
- Documented di Component Library page
- Published ke team library
Status: Spec ready untuk designer build Last updated: 2026-04-16 Version: 1.0