Meridian CRM
Audit UX & UI
1 · Ringkasan Eksekutif
Meridian punya fondasi visual di atas rata-rata: sistem token warna lengkap, hierarki heading benar, command palette yang matang, dan pola selection yang rapi. Craft-nya terlihat.
Masalahnya ada di lapisan lain. Dari tiga pekerjaan inti seorang sales, satu sudah jalan dengan baik dan dua belum ada:
| Pekerjaan inti | Status |
|---|---|
| Memindahkan deal ke stage berikutnya | BISA drag & drop berfungsi, termasuk keyboard |
| Membuka detail sebuah deal | TIDAK ADA tidak ada halaman/panel deal |
| Menutup deal sebagai Won / Lost | TIDAK ADA tidak ada stage Lost, Won tanpa kolom |
Ditambah satu masalah kepercayaan yang serius: filter periode di Overview mengubah label tapi tidak mengubah satupun angka.
4 hal yang harus diperbaiki lebih dulu
- Deal tidak punya halaman/panel detail. Empat jalur berbeda menuju sebuah deal, tidak satupun membukanya. Dua di antaranya justru menampilkan toast yang menyatakan deal itu “lives on the Pipeline board”.
- Filter periode di Overview tidak mengubah data apapun. Hanya labelnya yang berubah — dan label yang berubah itu membuat angka lama terbaca sebagai angka baru.
- Tidak ada stage “Lost”, dan “Won” tidak punya kolom di Board. Deal tidak bisa ditutup.
- Aplikasi tidak responsif sama sekali. Layout 4-kolom dan 12-kolom fixed di semua lebar layar.
2 · Scope & Metodologi
Halaman yang diaudit: / (Overview), /pipeline (Board + Table), /contacts, Contact drawer, New deal modal, Workspace settings modal, Command palette (⌘K).
Yang dilakukan:
- Walkthrough tiap halaman & tiap state interaktif yang bisa dijangkau
- Uji langsung: drag-drop board, submit form kosong, filter periode, saved views, bulk selection, command palette (query relevan + query kosong)
- Pengukuran DOM: dimensi elemen, computed style, transition duration, touch target
- Audit kontras terprogram terhadap seluruh text node yang terlihat, dihitung terhadap background efektif — bukan sampling manual
- Analisis stylesheet produksi (29 KB): media query, aturan
:hover/:focus-visible, token
Yang belum dicakup: usability testing dengan user asli · screen reader run-through end-to-end · performance & bundle audit · uji perangkat fisik (audit responsif di sini berbasis analisis CSS).
Bukan estimasi visual. Script verifikasi ada di Lampiran B — bisa dijalankan ulang kapan saja di DevTools Console.
3 · Bagian A — Audit UX
A1 · Task Flow — pekerjaan inti tidak bisa diselesaikan
Ini bagian paling penting dalam dokumen ini. Semua temuan lain sifatnya sekunder dibanding ini.
A1.1 CRITICAL Tidak ada detail deal
Tab Deals · 2 di dalam contact drawer bukan deal detail. Itu daftar deal milik seorang kontak: nama deal, badge stage, probabilitas, dan nilai. Berguna, tapi read-only dan bersifat ringkasan. Chevron › di ujung kanannya tidak membuka apapun.
Produk sendiri yang menyatakannya. Keempat jalur menuju sebuah deal diuji:
| Jalur masuk | Hasil |
|---|---|
| Klik kartu di Board | Membuka drawer kontak, bukan deal |
| Klik baris di Pipeline Table | Membuka drawer kontak (Caldera — Investor deck system → drawer Yusuf Osei) |
| Klik deal di tab Deals contact drawer | Toast: “Halcyon — Launch campaign” lives on the Pipeline board — tidak navigasi, tidak membuka apapun |
| Klik deal di command palette | Pindah ke /pipeline, toast: Veldt — Data-viz sprint — Lead on the board — tanpa highlight/scroll ke kartunya |
Dua dari empat jalur menampilkan pesan yang secara harfiah memberi tahu user bahwa deal itu hanya “ada di board”. Itu bukan bug — itu keputusan desain yang sudah disadari, dan produk memilih memberi tahu user daripada memberi mereka halamannya.
Masalah tambahan: aria-label kartu berbunyi "Arcadia — Ops dashboard, $24K" — screen reader menjanjikan sebuah deal, tapi aksinya membuka sebuah orang. Label/action mismatch, bukan sekadar navigasi yang salah.
Akibatnya user tidak bisa: melihat riwayat deal, mengubah value, mengubah close date, mencatat note yang menempel di deal, melihat siapa saja yang terlibat, atau menutup deal.
Rekomendasi: Buat drawer/route Deal (/deals/:id) berisi header (nama, value, stage editable, close date, owner), tab Activity, Contacts terkait, Notes, dan tombol Mark as Won / Mark as Lost. Sambungkan keempat jalur di atas ke sana — termasuk mengganti kedua toast dengan navigasi sungguhan.
A1.2 HIGH Win probability tersembunyi di satu tempat, read-only
Tab Deals di contact drawer menampilkan 25% likely dan 70% likely. Ini satu-satunya tempat di seluruh aplikasi yang memperlihatkan probabilitas menang.
Padahal KPI Weighted forecast $434K di Overview hanya bisa dihitung dari angka-angka ini. Jadi ada field yang menggerakkan salah satu metrik utama, tapi tidak muncul di kartu Board, tidak muncul di Pipeline Table, tidak ada di modal New deal, dan tidak bisa diedit di manapun.
Rekomendasi: Tampilkan probability sebagai kolom di Pipeline Table dan field editable di deal detail. Kalau nilainya otomatis dari stage, jelaskan aturannya — user perlu tahu kenapa forecast-nya $434K dan bukan $958K.
A1.3 CRITICAL Deal tidak bisa ditutup
Tiga masalah saling mengunci:
| Temuan | Bukti |
|---|---|
| Board hanya punya 4 kolom: Lead, Qualified, Proposal, Negotiation | Screenshot Board |
| Modal New deal menyediakan opsi stage Won | <select> Stage: Lead, Qualified, Proposal, Negotiation, Won |
| Tidak ada opsi “Lost” di manapun | Sama seperti di atas |
| Table menampilkan 2 deal ber-stage Won, Board tidak | Northbeam — Retail concept & Tidemark — Onboarding flow |
Buat deal dengan stage “Won” → deal itu langsung hilang dari Board dan tidak bisa diakses lagi lewat Board.
Dan: header Pipeline tertulis 16 open · $958K, tapi Table menampilkan 18 baris. Angka header tidak salah (16 memang open), tapi tabelnya tidak dilabeli dan tidak difilter — user melihat konflik.
Rekomendasi:
- Tambahkan stage
Lostdengan alasan wajib (Price / Timing / Competitor / No decision) — data ini yang bikin CRM berharga. - Di Board: tambahkan kolom
WondanLostyang collapsed by default, atau zona drop “Close deal” di kanan board. - Di Table: tambahkan label state (
Showing 18 deals · 16 open, 2 won) + filter open/closed.
A1.4 CRITICAL Filter periode di Overview tidak berfungsi
Test: This quarter → This month.
| Elemen | Sebelum | Sesudah |
|---|---|---|
| Pipeline value | $958K, +12% | $958K, +12% |
| Weighted forecast | $434K, +8% | $434K, +8% |
| Deals in motion | 16 | 16 |
| Avg. days to close | 34, −5 days | 34, −5 days |
| Sparkline (4 buah) | — | identik |
| Needs attention / Snapshot / Activity | — | tidak merespons |
| Label perbandingan | “vs last quarter” | “vs last month” ← satu-satunya yang berubah |
Label yang berubah justru memperburuk keadaan: user membaca “$958K, +12% vs last month” dan menganggapnya fakta. Ini bukan bug kosmetik — ini menyajikan angka yang salah.
Rekomendasi: Sambungkan filter ke data. Kalau belum bisa — nonaktifkan kontrolnya dan beri tooltip “Coming soon”. Kontrol yang mati lebih baik daripada kontrol yang berbohong.
A1.5 HIGH Empat KPI card adalah dead affordance
Keempat card punya role="button" dan tabindex="0" (dapat fokus keyboard), tapi klik tidak menghasilkan apapun.
Rekomendasi: Hubungkan tiap card ke drill-down yang relevan (Pipeline value → /pipeline; Deals in motion → pipeline difilter closing bulan ini), atau hapus role="button" sepenuhnya.
A2 · Information Architecture
A2.1 HIGH “Views” di sidebar diam-diam berpindah halaman
| View | Halaman tujuan | Indikasi di UI |
|---|---|---|
| Q3 Closing | /pipeline | tidak ada |
| Dormant > 30d | /contacts | tidak ada |
| Referrals | belum diuji | tidak ada |
Dot warna di sebelah kiri (hijau/oranye/biru) tidak memetakan ke halaman tujuan — jadi warnanya terlihat dekoratif dan malah menambah beban baca.
Rekomendasi: Kelompokkan dengan label (DEALS / PEOPLE), atau tempelkan ikon halaman tujuan pada tiap view. Ganti dot acak menjadi ikon yang bermakna.
A2.2 MEDIUM Isi “Q3 Closing” tidak sesuai namanya
View Q3 Closing menampilkan Juniper & Vale — Naming + identity dengan close date Oct 7 — itu Q4.
A2.3 HIGH “Needs attention” tidak diurutkan berdasarkan urgensi
Elena Brandt 6d
Yusuf Osei 12d
June Park 9d
Tomás Rivera 21d
Harriet Boyle 16d
Bukan urut hari (6, 12, 9, 21, 16), bukan urut tanggal sumber (Jul 31, 25, 28, 16, 21), bukan urut nilai deal. Badge harinya sudah punya sistem warna (abu → amber → merah), tapi urutannya tidak mengikuti warnanya sendiri — user melihat merah di posisi 4 dan abu di posisi 1.
Modul ini satu-satunya bagian di Overview yang tugasnya memprioritaskan. Kalau urutannya acak, modulnya tidak melakukan pekerjaannya.
Rekomendasi: Default sort by severity (hari terlama × nilai deal), tampilkan kontrol sort, dan jelaskan aturannya (Sorted by risk).
A2.4 HIGH Pencarian mengembalikan hasil yang tidak relevan
Query north mengembalikan:
| Hasil | Relevan? |
|---|---|
| Elena Brandt · Northbeam Goods | ya |
| Owen Gallagher · Northbeam Goods | ya |
| Dr. Amara Singh · Marrow Health | tidak |
| Northbeam — Brand system | ya |
| Northbeam — Retail concept | ya |
| Marrow — Patient portal UX | tidak |
| Marrow — Brand refresh | tidak |
3 dari 7 hasil tidak mengandung kata kunci. Fuzzy matching-nya terlalu longgar — kemungkinan subsequence match lintas field.
Rekomendasi: Naikkan threshold, batasi fuzzy ke prefix/word-boundary, dan tampilkan highlight bagian yang match supaya user paham kenapa sebuah hasil muncul.
A3 · Feedback, State & Error Handling
| # | Temuan | Severity | Bukti |
|---|---|---|---|
| A3.1 | Aksi baris tersembunyi total saat rest state — 40 kontrol ber-opacity:0 di /contacts (20 checkbox + 20 tombol Copy email), plus 15 tombol Email/Task/Snooze di Needs attention | HIGH | class .reveal, muncul hanya pada :hover/:focus-within |
| A3.2 | Karena A3.1, modul Needs attention terlihat sebagai daftar bacaan pasif — padahal isinya tiga aksi paling berguna di seluruh aplikasi | HIGH | — |
| A3.3 | Checkbox “select all” tidak masuk state indeterminate saat 1 dari 20 baris terpilih | MED | thead input.checked === false |
| A3.4 | Bulk action bar menutupi baris summary footer (20 rows · Σ $958K · ⌀ 12d) | MED | Screenshot |
| A3.5 | Submit form New deal kosong tidak memunculkan error inline — hanya native validation bubble | MED | field required, tanpa UI error |
| A3.6 | Empty state pencarian (No results for "zzzz") polos — tanpa saran atau aksi lanjutan | LOW | — |
| A3.7 | Dot aktivitas basi (○) di kartu Board hanya punya native title — tanpa legenda, tanpa aria-label. Tidak terlihat di touch, tidak terbaca screen reader | MED | <span class="rounded-full" title="No activity for 12 days"> tanpa accessible name |
Rekomendasi utama: Untuk baris yang menjadi fokus utama (Needs attention), tampilkan aksi secara permanen — jangan sembunyikan di balik hover. Pola hover-reveal cocok untuk tabel padat, tidak cocok untuk modul triase. Tetap sediakan fallback non-hover di semua tempat — perangkat touch tidak punya hover sama sekali.
A4 · Modal “New deal”
role="dialog" + aria-modal="true" · semua <label> membungkus input-nya (asosiasi valid tanpa for/id) · field Deal name required · default close date 30 hari ke depan (2026-09-05 — sudah diverifikasi benar; tampilan 05/09/2026 hanya format locale DD/MM, bukan tanggal lampau).
| # | Temuan | Severity |
|---|---|---|
| A4.1 | Tidak ada field Contact/People. Deal dibuat tanpa terhubung ke orang — padahal panel kontak adalah satu-satunya tempat deal bisa dilihat detailnya | HIGH |
| A4.2 | Company default ke item pertama (Northbeam Goods), bukan placeholder. User yang buru-buru akan membuat deal di company yang salah | HIGH |
| A4.3 | Tidak ada penanda field mana yang wajib | MED |
| A4.4 | Stage “Won” tersedia tapi tidak punya kolom di Board (lihat A1.3) | CRIT |
A5 · Modal “Workspace settings”
| # | Temuan | Severity |
|---|---|---|
| A5.1 | Konten terpotong di balik sticky footer — baris member terakhir (Sofía Reyes) terpotong separuh, tanpa indikasi batas scroll | MED |
| A5.2 | Plan Pro · 12 seats vs MEMBERS · 5 members tanpa konteks — 7 seat kosong tidak dijelaskan | LOW |
| A5.3 | Link “Manage” ×5 tanpa indikasi tujuan | LOW |
4 · Bagian B — Validasi 10 Komentar Ruttl
Semua 10 komentar berhasil diambil beserta selector elemen yang di-anchor, jadi validasi di bawah menunjuk ke elemen yang tepat — bukan tebakan.
| # | Halaman | Komentar | Verdict |
|---|---|---|---|
| 1 | /contacts | ukuran beda dengan profile user | VALID — diagnosis perlu dikoreksi |
| 2 | /contacts | checkbox tidak muncul | VALID discoverability, bukan bug |
| 3 | /contacts | hug content | VALID |
| 4 | /contacts | padding atas bawah kolom terlalu sempit | VALID |
| 5 | /pipeline | better diberi pagination | VALID BERSYARAT |
| 6 | /contacts | arrow tidak muncul | PERLU KONFIRMASI |
| 7 | / | tinggi content tdk balance, better disamakan | VALID solusinya beda |
| 8 | /contacts | arrow tidak muncul | DUPLIKAT #6 |
| 9 | /contacts | micro interaction | VALID lebih luas dari dugaan |
| 10 | /contacts | better diberi pagination | DUPLIKAT #5, VALID |
tbody > tr:1 > td:4 > span > span:1 — badge company di kolom COMPANY
| Elemen | Ukuran | Font | Bentuk |
|---|---|---|---|
| Avatar orang (kolom NAME) | 24 × 24 | 10px | lingkaran |
| Avatar owner (kolom OWNER) | 24 × 24 | 10px | lingkaran |
| Badge company (kolom COMPANY) | 18 × 18 | 8px | rounded-square (r 4px) |
Verdict: valid — tapi jangan disamakan
Perbedaan bentuk (kotak vs lingkaran) justru pola yang bagus — itu konvensi standar untuk membedakan organisasi dari orang. Pertahankan.
Masalah sebenarnya adalah font 8px. Itu di bawah batas keterbacaan praktis untuk teks apapun, bahkan inisial.
Rekomendasi: Naikkan company badge ke 20 × 20 dengan font 10px, pertahankan radius 4px. Perbedaan 20 vs 24 tetap membentuk hierarki tanpa mengorbankan keterbacaan.
tbody > tr:1 > td:1 > input
Verdict: valid sebagai masalah discoverability — bukan bug fungsional
Checkbox ada di DOM dengan opacity: 0 dan class .reveal. Aturan CSS-nya:
.reveal → opacity: 0
.reveal-parent:hover .reveal → opacity: 1
.reveal-parent:focus-within .reveal → opacity: 1
State terpilih sebenarnya sudah ditangani dengan baik. Setelah dicentang, baris mendapat background --ember-50 (#fdf3ec), checkbox tetap terlihat, dan muncul bulk action bar 1 selected · Add tag · Assign · Export · Delete. Bagian itu solid — jangan diubah.
Masalah yang sesungguhnya
- Checkbox “select all” di header selalu terlihat (opacity 1), sedangkan checkbox baris tidak. Inkonsistensi ini yang bikin user berpikir fiturnya rusak — header menjanjikan bulk selection, baris tidak menunjukkan caranya.
- Di perangkat touch, hover tidak ada. Bulk selection mustahil ditemukan di tablet/mobile.
- Total ada 40 kontrol tersembunyi di halaman ini (20 checkbox + 20 tombol Copy email).
Rekomendasi
- Tampilkan checkbox baris pada
opacity: 0.35saat rest,1saat hover/focus/checked. Tetap tenang secara visual, tapi terlihat ada. - Atau: tampilkan permanen saat ada ≥1 baris terpilih (mode seleksi aktif).
- Wajib:
@media (hover: none) { .reveal { opacity: 1 } }
main > div > div:2 — wrapper tabel
Verdict: valid
Wrapper punya class flex-1 overflow-auto → selalu meregang setinggi viewport terlepas dari jumlah baris.
| Kondisi | Efek |
|---|---|
| 20 baris (default) | Tabel 790px di dalam container 676px → scroll internal |
| 2 baris (view Dormant > 30d) | Container tetap penuh → ~500px area kosong di bawah data |
Rekomendasi: Ganti flex-1 dengan max-h-[…] h-auto. Container shrink-wrap ke konten sampai batas maksimum, lalu baru scroll. Ini juga otomatis memperbaiki “kotak kosong besar” di semua saved view yang hasilnya sedikit.
tbody > tr:1 > td:4
Verdict: valid — dan lebih ekstrem dari dugaan
Pengukuran: padding-top: 0px, padding-bottom: 0px pada semua <td>. Tinggi baris 36px murni berasal dari line-height.
36px untuk sel berisi dua baris teks (nama + email) itu sangat padat. Jarak vertikal antar baris data praktis nol.
Rekomendasi:
- Density Comfortable (default):
padding: 10px 12px, tinggi baris ~48px - Density Compact (power user):
padding: 6px 12px, tinggi baris ~40px - Sediakan toggle density di toolbar tabel — dua kebutuhan ini nyata dan berbeda
#5 → main > div · #10 → tfoot > tr > td:1
Verdict: valid sebagai antisipasi skala — belum jadi masalah pada data saat ini
Kondisi sekarang: Contacts 20 baris, Pipeline Table 18 baris. Keduanya masih nyaman dengan scroll internal. Tapi tidak ada pagination, virtualisasi, maupun “load more” — pada 500 kontak halaman ini akan berat.
Rekomendasi: Untuk data-tabel CRM, virtualisasi + infinite scroll lebih tepat daripada pagination bernomor — sales bekerja dengan scanning, bukan browsing halaman. Pertahankan sticky footer summary (Σ $958K · ⌀ 12d) karena itu justru nilai tambahnya. Kalau tetap ingin pagination, taruh di footer bersama summary — jangan menggantikannya.
html — ruttl gagal me-resolve elemen aslinya
Ruttl sendiri menandai salah satunya dengan “This comment may point to a missing element.” Jadi lokasi persis yang kamu maksud tidak bisa dikonfirmasi.
Yang bisa diverifikasi
| Lokasi | Kondisi arrow |
|---|---|
| Header tabel Contacts | Chevron sort ada — opacity: 0.5 untuk kolom non-aktif, 1 untuk kolom aktif (Last touch ↑) |
| Header tabel Pipeline | Tidak ada chevron sort sama sekali — tabel ini tidak sortable |
<th> di kedua tabel | Tidak ada atribut aria-sort |
Dugaan terkuat: yang kamu maksud adalah chevron sort di Contacts yang terlalu samar (opacity 0.5 di atas warna yang sudah lemah) sehingga terbaca “tidak ada”.
Rekomendasi (berlaku untuk semua skenario):
- Naikkan chevron non-aktif ke
opacity: 0.7dan gelapkan warnanya - Tambahkan
aria-sort="ascending|descending|none"pada tiap<th> - Buat Pipeline Table sortable — saat ini ada dua tabel dengan kemampuan berbeda tanpa alasan jelas
Arrow yang mana persisnya? Kalau bukan sort chevron, kemungkinan lain adalah chevron › pada kartu deal di dalam contact drawer.
main > div > div:2 — grid dua kolom Overview
Verdict: valid — tapi solusinya bukan “disamakan”
| Kolom | Isi | Tinggi |
|---|---|---|
Kiri (col-span-7) | Needs attention | 495px |
Kanan (col-span-5) | Pipeline snapshot (207px) + Recent activity (319px) + gap | 541px |
| selisih | 46px | |
Grid memakai items-start, jadi kolom tidak stretch dan tepi bawahnya bergerigi.
Kenapa “disamakan” bukan solusi terbaik: kalau dipaksa items-stretch, card yang lebih pendek akan punya ruang kosong di dalamnya — itu memindahkan masalah, bukan menyelesaikannya.
Rekomendasi, urut dari yang paling disarankan:
- Seimbangkan lewat konten — tambah 1 item di Needs attention (6 flagged, bukan 5) atau kurangi 1 item Recent activity. Selisih 46px ≈ tinggi satu baris activity.
- Beri
min-heightyang sama pada kedua kolom, biarkan yang lebih pendek punya padding bawah yang disengaja. - Kalau tetap ingin rata:
items-stretchdan buat Recent activity scrollable di dalam card-nya — jadi ruang ekstra terpakai, bukan kosong.
aside > header > div:2 — tombol ikon (Email / Schedule) di header contact drawer
Verdict: valid — dan masalahnya jauh lebih luas dari titik yang kamu tandai
Ironisnya, tombol yang kamu tandai justru termasuk yang sudah benar: transition 0.12s, dan drawer-nya punya animasi meridian-slide-in.
Yang sebenarnya bermasalah adalah keseluruhan aplikasi:
| Metrik | Angka |
|---|---|
| Total elemen interaktif (button / role=button / a / input / select) | 103 |
Punya transition-duration: 0s | 79 (77%) |
| Contoh | seluruh nav sidebar, seluruh saved views, status pill di drawer — semua all 0s |
Jadi: 77% interaksi di aplikasi ini berpindah state secara instan — hover, active, dan selected muncul seperti kedipan, bukan respons.
Rekomendasi:
- Terapkan transisi dasar konsisten:
transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease - Jangan transisi
all— mahal dan sering menimbulkan efek tak terduga - Prioritaskan: nav sidebar (paling sering disentuh), baris tabel, kartu board
- Sudah ada
@media (prefers-reduced-motion: reduce)— bagus, pertahankan dan pastikan aturan baru ikut di-cover
Tidak satupun dari 10 komentar menyentuh A1.1–A1.4 — deal tanpa detail, filter periode kosmetik, deal tidak bisa ditutup. Sepuluh komentar ini semuanya di lapisan visual/polish. Kalau prioritas engineering diambil dari daftar ini saja, produk akan terlihat lebih rapi tapi tetap tidak bisa menutup satupun deal.
5 · Bagian C — Audit UI
C1 · Sistem Warna & Token KEKUATAN UTAMA
Token terdefinisi rapi dan konsisten dipakai:
ink : 900 #1a1915 · 700 #4d4a41 · 500 #767266 · 300 #a8a498
surface : 0 #fdfcfa · 1 #f7f5f1 · 2 #f0ede7 · 3 #e8e4dc
border : subtle #e9e5dd · strong #d6d1c6
ember : 700 #a3360e · 600 #c2410c · 500 #e05a1f · 100 #fce9dd · 50 #fdf3ec
semantic : positive #2e7d4f · warning #a86a0f · danger #b3322e · info #3d5fa8
chip : clay · ochre · sage · pine · iris · slate (pasangan text+bg)
Palet warm-neutral ini membedakan Meridian dari CRM lain yang cenderung biru-korporat. Ini aset — pertahankan.
C2 · Kontras 5 PASANGAN GAGAL WCAG AA
Diukur terhadap seluruh text node yang terlihat, dihitung terhadap background efektif:
| Teks | Foreground | Background | Size | Rasio | Butuh | Instance |
|---|---|---|---|---|---|---|
from email thread · Jul 31 | #a8a498 | #fdfcfa | 11.5px | 2.43:1 | 4.5:1 | 5 |
Badge 12d | #a86a0f | #f8edd8 | 11.5px | 3.82:1 | 4.5:1 | 2 |
+12% +8% −5 days | #2e7d4f | #e1f0e6 | 11.5px | 4.28:1 | 4.5:1 | 3 |
Sidebar: Studio North, Views, Command palette, Workspace settings | #767266 | #f7f5f1 | 11–12.5px | 4.41:1 | 4.5:1 | 4 |
Pill tanggal di kartu Board (Sep 23, dll) | #767266 | #f0ede7 | 11px | 4.11:1 | 4.5:1 | 11 |
--ink-300 pada 2.43:1Dipakai untuk baris provenance di Needs attention (from email thread · Jul 31) — informasi yang justru membuat modul itu bisa dipercaya. Saat ini hampir tidak terbaca.
| Token | Sekarang | Ganti ke | Rasio baru |
|---|---|---|---|
--ink-300 | #a8a498 | #8a8578 | ~4.6:1 |
--ink-500 | #767266 | #6b6759 | ~5.1:1 |
--positive-600 | #2e7d4f | #256b43 | ~5.2:1 |
--warning-600 | #a86a0f | #8a5709 | ~5.0:1 |
Empat perubahan token ini memperbaiki 25 instance sekaligus tanpa menyentuh satupun komponen. Rasio effort:impact terbaik yang ada di sini.
C3 · Tipografi
| Temuan | Detail | Severity |
|---|---|---|
| Font 8px pada company badge | Di bawah batas keterbacaan apapun | HIGH |
| Body text 11–11.5px sangat dominan | Metadata, badge, pill, provenance semua 11.5px | MED |
| Skala heading rapi | h1 → h2 konsisten, hierarki jelas | OK |
| Monospace untuk angka/tanggal | Konsisten & tepat guna | OK |
Rekomendasi: Naikkan lantai ukuran font ke 11px absolut, dan pindahkan teks yang membawa makna (provenance, badge hari) ke 12px. 11.5px untuk semua metadata menghilangkan hierarki di dalam metadata itu sendiri.
C4 · Konsistensi Komponen
| # | Inkonsistensi | Detail |
|---|---|---|
| C4.1 | Status pill vs tag chip identik | Callum Doyle menampilkan Champion · Champion · Referral — satu status, satu tag, bentuknya sama persis. User tidak bisa membedakan mana status (tunggal, terkontrol) dan mana tag (jamak, bebas) |
| C4.2 | Dua tabel, dua kemampuan | Contacts: sortable + footer summary + row checkbox. Pipeline: tidak satupun |
| C4.3 | Company vs owner avatar di kartu Board | Ditumpuk vertikal dengan treatment mirip — sulit membedakan mana perusahaan, mana pemilik deal |
| C4.4 | Dot warna saved view | Tidak memetakan ke apapun yang bisa dipahami |
Rekomendasi C4.1: Bedakan secara struktural — status sebagai pill solid (chip-*-bg penuh), tag sebagai outline chip dengan border dan background transparan. Atau pisahkan barisnya dengan label mikro (Status / Tags).
C5 · Spacing & Layout
| # | Temuan | Bukti | Severity |
|---|---|---|---|
| C5.1 | <td> padding vertikal = 0 | measured 0px | HIGH |
| C5.2 | Kolom Board tetap tinggi penuh walau hanya 1 kartu | filter “My deals” | MED |
| C5.3 | Kolom Overview 495px vs 541px | measured | LOW |
| C5.4 | Kolom Board ke-4 (Negotiation) terpotong di 1440px — $15… bukan $150K | Screenshot | MED |
| C5.5 | Tidak ada affordance “tambah deal ke stage ini” di area kolom kosong | — | LOW |
C7 · Responsive PRAKTIS TIDAK ADA
Analisis stylesheet produksi (29 KB):
| Media query | Jumlah pemakaian | Untuk apa |
|---|---|---|
(prefers-reduced-motion: reduce) | 1 | aksesibilitas motion OK |
(hover: hover) | 2 | hover states |
(min-width: 40rem) | 1 | margin atas modal (sm:mt-0) |
Itu satu-satunya breakpoint layout di seluruh aplikasi.
KPI row : grid-cols-4 → 272px 272px 272px 272px (fixed, semua lebar layar)
Main layout : grid-cols-12 → 80px × 12 (fixed, semua lebar layar)
Sidebar : w-60 shrink-0 (fixed 240px, tidak bisa collapse)
Drawer : w-[480px] (fixed)
Ada <meta name="viewport" content="width=device-width, initial-scale=1.0"> — jadi browser mobile tidak akan zoom-out otomatis. Di layar 390px: 4 KPI card masing-masing ~60px, angka $958K akan terpotong atau overflow, sidebar 240px memakan 62% layar.
Rekomendasi bertahap:
| Breakpoint | Perubahan |
|---|---|
| < 640px | Sidebar → drawer off-canvas · KPI grid-cols-2 · main grid-cols-1 · Board scroll horizontal 1 kolom penuh · drawer w-full |
| 640–1024px | Sidebar collapse ke icon-only (64px) · KPI grid-cols-2 · main grid-cols-1 |
| 1024–1440px | Sidebar penuh · KPI grid-cols-4 · main grid-cols-12 (kondisi sekarang) |
| Tabel | Pada mobile: ubah baris jadi card stack atau sembunyikan kolom sekunder |
6 · Bagian D — Aksesibilitas
Yang sudah benar DI ATAS RATA-RATA
| Aspek | Bukti |
|---|---|
| Kartu deal punya semantik & nama | role="button" + tabindex="0" + aria-label="Arcadia — Ops dashboard, $24K" |
| Modal & drawer | role="dialog" + aria-modal="true" + aria-label="Callum Doyle record" |
| Label form | Semua <label> membungkus input-nya — asosiasi valid tanpa for/id |
| Tombol ikon punya nama deskriptif | aria-label="Snooze Elena Brandt for 3 days" — bukan generik |
| Checkbox baris | aria-label="Select Callum Doyle" |
| Kontrol tersembunyi tetap bisa keyboard | .reveal merespons :focus-within, bukan hanya :hover |
| Focus styling | 4 aturan :focus-visible di stylesheet |
| Hierarki heading Overview | h1: Overview → h2: Needs attention / Pipeline snapshot / Recent activity |
| Motion | @media (prefers-reduced-motion: reduce) ada |
Ini sudah di atas rata-rata untuk produk sekelas ini. Kredit di tempat yang layak.
Yang perlu diperbaiki
| # | Temuan | WCAG | Severity |
|---|---|---|---|
| D1 | 5 pasangan kontras gagal AA (25 instance) — lihat C2 | 1.4.3 | HIGH |
| D2 | Touch target di bawah 44×44: tombol Needs attention 35×28, tombol drawer header 36×28, checkbox baris 13×13 | 2.5.5 | MED |
| D3 | Tidak ada aria-sort pada <th> yang sortable | 4.1.2 | MED |
| D4 | <th> sortable tidak punya role="button" / tidak jelas dapat diaktifkan keyboard | 2.1.1 | MED |
| D5 | Drawer aria-modal="true" tanpa scrim — konten di belakang masih pointer-events: auto dan bisa diklik. Screen reader menyembunyikan latar, pengguna awas tidak | 4.1.2 | MED |
| D6 | Dot aktivitas basi hanya title — tanpa accessible name | 1.1.1 | MED |
| D7 | Header checkbox tidak punya state indeterminate | 4.1.2 | LOW |
| D8 | Kolom Board (Lead, Qualified, …) bukan heading — hanya <h1>Pipeline</h1> di seluruh halaman | 1.3.1 | LOW |
| D9 | Tidak ada dark mode (0 aturan prefers-color-scheme) meski token system sudah siap | — | LOW |
Token --overlay-scrim: rgba(26,25,21,.2) sudah ada di stylesheet tapi tidak dipakai untuk drawer. Perbaikan satu baris.
7 · Prioritas & Rekomendasi
P0 Blokir rilis
| # | Item | Ref | Est. |
|---|---|---|---|
| 1 | Deal detail (drawer atau route) + sambungkan keempat jalur masuk | A1.1 | L |
| 2 | Stage Lost + kolom Won/Lost di Board | A1.3 | M |
| 3 | Sambungkan filter periode ke data — atau nonaktifkan | A1.4 | S / XS |
| 4 | Perbaiki 4 token warna (memperbaiki 25 instance kontras) | C2 | XS |
Item #4 adalah quick win terbesar di dokumen ini: 4 baris CSS, 25 pelanggaran WCAG selesai.
P1 Sebelum user testing
| # | Item | Ref |
|---|---|---|
| 5 | Tampilkan win probability di Pipeline Table + jadikan editable | A1.2 |
| 6 | Sort default berbasis urgensi di Needs attention | A2.3 |
| 7 | Tampilkan aksi Needs attention secara permanen (jangan hover-only) | A3.1 |
| 8 | Fallback non-hover untuk semua .reveal (@media (hover: none)) | A3.1 |
| 9 | Perbaiki relevance pencarian | A2.4 |
| 10 | Field Contact di modal New deal + placeholder untuk Company | A4.1, A4.2 |
| 11 | Padding sel tabel + toggle density | Ruttl #4 |
| 12 | Company badge 20×20 @10px | Ruttl #1 |
| 13 | Table wrapper hug content | Ruttl #3 |
| 14 | Pipeline Table sortable + footer summary | C4.2 |
| 15 | Beri label halaman tujuan pada saved views | A2.1 |
| 16 | KPI card: sambungkan ke drill-down atau hapus role="button" | A1.5 |
| 17 | Scrim untuk drawer (token sudah ada) | D5 |
P2 Polish
| # | Item | Ref |
|---|---|---|
| 18 | Transisi 120ms pada seluruh elemen interaktif | Ruttl #9 |
| 19 | Breakpoint responsif (mobile / tablet / desktop) | C7 |
| 20 | Bedakan status pill vs tag chip secara visual | C4.1 |
| 21 | aria-sort + role="button" pada <th> | D3, D4 |
| 22 | Touch target minimum 44×44 | D2 |
| 23 | Legenda untuk dot aktivitas basi | A3.7, D6 |
| 24 | State indeterminate pada select-all | A3.3, D7 |
| 25 | Empty state pencarian dengan saran | A3.6 |
| 26 | Perbaiki logika filter view “Q3 Closing” | A2.2 |
| 27 | Dark mode (token sudah siap) | D9 |
8 · Yang Sudah Bagus — Jangan Diubah
Audit ini berat di sisi kritik, jadi bagian ini perlu ditegaskan.
- Drag & drop di Kanban board. ★ HIGHLIGHT Memakai dnd-kit dengan aksesibilitas aktif:
aria-roledescription="Draggable deal card", instruksi keyboard diaria-describedby, dan 3 live region yang mengumumkan setiap perpindahan. Keyboard drag (Space→ arrow →Space,Escbatal) berfungsi penuh dan sudah diuji end-to-end:
Ini bagian yang paling sering dikerjakan setengah jadi di produk lain — di sini justru lengkap. Jangan diutak-atik.Space → "Draggable item d-arcadia-ops was moved over droppable area d-arcadia-ops." ArrowRight → "Draggable item d-arcadia-ops was moved over droppable area d-pomelo-refresh." - Sistem token warna. Lengkap, konsisten, dan palet warm-neutral-nya membedakan produk ini dari CRM biru-korporat pada umumnya.
- Tab “Deals” di contact drawer. Menampilkan stage, win probability, dan nilai dalam satu baris ringkas. Konsepnya benar — tinggal disambungkan ke deal detail.
- Command palette (⌘K). Grouping (Quick actions / Navigation / People / Deals), keyboard hints di footer, empty state ada. Bagian paling matang di seluruh aplikasi.
- Pola selection. Background
--ember-50pada baris terpilih + bulk bar (Add tag · Assign · Export · Delete) — sudah tepat. - Provenance di Needs attention.
from email thread · Jul 31menjelaskan kenapa sesuatu di-flag. Ini yang bikin modul AI-flagging bisa dipercaya. Ide bagus, tinggal kontrasnya diperbaiki. - Footer summary tabel Contacts.
Σ $958K · ⌀ 12d— sticky, ringkas, berguna. Tiru pola ini di Pipeline. aria-labelyang deskriptif."Snooze Elena Brandt for 3 days", bukan"Snooze". Detail kecil yang sering dilewatkan..revealmerespons:focus-within. Pengguna keyboard tidak terkunci dari kontrol tersembunyi. Ini dipikirkan, bukan kebetulan.prefers-reduced-motionsudah ada.- Default close date 30 hari ke depan pada modal New deal — sudah benar, bukan tanggal lampau seperti yang mungkin terlihat dari format
05/09/2026.
Lampiran A · Ringkasan Angka
| Metrik | Nilai |
|---|---|
Kartu deal dengan aria-roledescription="Draggable deal card" | 18 |
| Live region untuk pengumuman drag | 3 |
Elemen HTML5 draggable di Board | 0 — tidak relevan, dnd-kit pakai Pointer Events |
| Jalur masuk ke deal yang membuka deal detail | 0 dari 4 |
| Media query layout di seluruh CSS | 1 (min-width: 40rem, dipakai 1×) |
| Elemen interaktif total | 103 |
Tanpa transisi (0s) | 79 (77%) |
Kontrol tersembunyi (opacity: 0) di /contacts | 40 |
| Pasangan kontras gagal WCAG AA | 5 pasangan / 25 instance |
| Kontras terburuk | 2.43:1 (--ink-300 @11.5px, butuh 4.5:1) |
Padding vertikal <td> | 0px |
| Font terkecil | 8px (company badge) |
Aturan prefers-color-scheme | 0 |
| Ukuran stylesheet produksi | 29 KB |
Lampiran B · Cara Verifikasi Ulang
Jalankan di DevTools Console pada halaman terkait:
// ⚠️ JANGAN pakai ini untuk mengecek drag-and-drop — hasilnya false negative.
// dnd-kit / Pragmatic DnD berbasis Pointer Events, tidak memakai atribut draggable.
document.querySelectorAll('[draggable]').length // → 0 (menyesatkan)
// Cara yang benar: cek kontrak aksesibilitas DnD
document.querySelectorAll('[aria-roledescription]').length // → 18 kartu draggable
document.getElementById('DndDescribedBy-0').textContent // → instruksi keyboard drag
// Uji keyboard drag beneran (jalankan di /pipeline)
const c = [...document.querySelectorAll('[role=button]')]
.find(e => e.getAttribute('aria-roledescription') === 'Draggable deal card');
c.focus();
const key = k => c.dispatchEvent(new KeyboardEvent('keydown',
{ key: k, code: k === ' ' ? 'Space' : k, keyCode: k === ' ' ? 32 : 39,
bubbles: true, cancelable: true }));
key(' '); key('ArrowRight');
[...document.querySelectorAll('[aria-live]')].map(e => e.textContent).filter(Boolean)
// → "Draggable item d-arcadia-ops was moved over droppable area d-pomelo-refresh."
// Breakpoint responsif
fetch(document.querySelector('link[rel=stylesheet]').href)
.then(r => r.text())
.then(c => console.log([...c.matchAll(/@media[^{]+/g)].map(m => m[0])))
// → prefers-reduced-motion, hover:hover ×2, min-width:40rem
// Grid fixed
[...document.querySelectorAll('*')]
.filter(e => getComputedStyle(e).display === 'grid')
.map(e => getComputedStyle(e).gridTemplateColumns)
// → "272px 272px 272px 272px", "80px × 12"
// Elemen tanpa transisi
[...document.querySelectorAll('button,[role=button],a,input,select')]
.filter(e => getComputedStyle(e).transitionDuration === '0s').length // → 79
// Kontrol tersembunyi (di /contacts)
[...document.querySelectorAll('.reveal')]
.filter(e => getComputedStyle(e).opacity === '0').length // → 40
// Padding sel tabel
getComputedStyle(document.querySelector('tbody td')).paddingTop // → "0px"