Files
zarinpal-analytics-frontend/DESIGN.md

12 KiB
Raw Blame History

Design System — ZarinPal Merchant Analytics

  • Status: Draft baseline for approval
  • Version: 0.2.1
  • Last updated: 2026-08-20
  • Direction: Persian-first RTL

Design intent

Match the recognizable product language of ZarinPal without copying inaccessible proprietary assets: calm financial confidence, generous whitespace, crisp white surfaces, strong blue actions, restrained yellow accents, dark ink sections, rounded geometry, and dense data presented in plain language.

The authenticated panel screenshots are the primary behavioral reference: fixed right navigation on desktop, right-side drawer on narrow screens, light gray workspace, white cards, compact outlined controls, blue active states, and direct numerical summaries. The public-site screenshots inform larger radius, dark promotional surfaces, and spacing—but the dashboard prioritizes speed and legibility over marketing spectacle.

Foundations

Color tokens

Token Value Use
--color-brand-600 #0A33FF Primary actions, active navigation, links, selected chart series
--color-brand-700 #0828CC Hover/pressed brand state
--color-accent-400 #FFD400 Small ZarinPal-like accent, highlights; never body text on white
--color-ink-950 #19191A Primary text, dark panels
--color-ink-700 #3D3D42 Secondary strong text
--color-ink-500 #6B6B73 Supporting text
--color-canvas #F1F1F3 Application background
--color-surface #FFFFFF Cards, drawer, dialogs
--color-surface-muted #F7F7F8 Nested panels, table headers
--color-border #E3E3E7 Dividers and neutral outlines
--color-success-600 #248061 Success states and positive semantic data
--color-warning-600 #A86700 Warnings; paired with text/icon, never color alone
--color-danger-500 #FF5C5D Errors and destructive actions
--color-info-surface #E7F2FF Informational callouts

All text/background pairs must meet WCAG 2.2 AA contrast. Chart palettes require patterns, labels, or shapes in addition to hue.

Typography

  • Preferred Persian UI family: a legally distributable Yekan-family webfont if supplied/licensed for this project.
  • Safe open fallback: Vazirmatn, then Tahoma, system sans-serif.
  • Use tabular numerals for tables/KPIs where supported.
  • Body: 14–16 px, line-height 1.7–1.9 for Persian.
  • Labels/meta: 12–14 px; never below 12 px.
  • Section heading: 20–24 px, weight 700.
  • Page heading: 28–32 px desktop, 24–28 px mobile, weight 700–800.
  • Avoid ultralight weights and excessive bolding. Keep Persian punctuation and half-spaces correct.

Spacing, shape, elevation

  • Base spacing unit: 4 px; common sequence 4, 8, 12, 16, 24, 32, 48, 64.
  • Control height: 40 px desktop; interactive touch target at least 44×44 px on touch layouts.
  • Card radius: 12 px for dense dashboard cards; 16–20 px for feature/empty-state surfaces.
  • Button/input radius: 8–10 px; pill radius only for compact statuses and segmented controls.
  • Card border is preferred over shadow. Default elevation is subtle (0 1px 3px rgb(25 25 26 / 0.08)); overlays may use stronger elevation.
  • Content width is bounded for scanning; tables/charts may use the full available dashboard column.

Layout and responsiveness

Desktop (≥ 1024 px)

  • Fixed right sidebar, approximately 248–272 px wide.
  • Top utility bar remains visually quiet.
  • Main content uses a centered, fluid container with 24–32 px gutters.
  • Overview uses a 12-column grid. Primary analytics get 8 columns; secondary summaries get 4 where useful.
  • Do not create large empty regions merely to mimic the reference screenshot; use the density required by real content.

Tablet (768–1023 px)

  • Sidebar becomes an overlay drawer from the right.
  • Two-column cards collapse according to content, not arbitrary device labels.
  • Tables may scroll horizontally only after priority columns and responsive alternatives are considered.

Mobile (< 768 px)

  • Single-column content, 16 px outer gutter.
  • Right-side navigation drawer traps focus, closes with Escape/backdrop, restores focus to its trigger, and locks background scroll.
  • KPI pairs stack; charts remain at least 240 px tall and expose an accessible data table/summary.
  • Sticky actions must not cover content or browser safe areas.

Navigation and information architecture

Initial authenticated routes:

  1. ورود (login)
  2. پیشخوان (overview)
  3. عملکرد پرداخت (success, retries, failures, timing)
  4. کارمزد و درآمد (real merchant fee amounts and rates in Rial)
  5. مقایسه با هم‌گروه‌ها (anonymized category/cohort benchmark)
  6. کیفیت داده / درباره شاخص‌ها (definitions, freshness, limitations)
  7. حساب کاربری / خروج

Navigation labels should describe merchant tasks, not internal database concepts. Active location is conveyed by color plus a bar/background and aria-current.

Core component contracts

  • App shell: sidebar/drawer, utility header, page title, merchant identity, analytics progress/status. First access idempotently starts a run, then opens its authenticated SSE stream; the page reveals overview, payment performance, fees, and peer comparison independently as each becomes ready.
  • Metric card: label, formatted value/unit, comparison and period, semantic trend, definition link, independent queued/calculating/ready/failed states.
  • Chart card: title, period/filter controls, concise takeaway, plot, legend, accessible tabular alternative, provenance link, and section-local loading/error behavior.
  • Insight card: deterministic finding, numeric evidence, suggested merchant action, confidence/limitation; AI-generated copy is visibly labeled later.
  • Data table: semantic headers, server pagination/sorting, row actions named for screen readers, responsive priority, empty/error states.
  • Filter bar: URL-backed state, explicit apply/reset behavior, and approved daily, weekly, Jalali-month, and Persian-season periods with unambiguous comparison baselines.
  • Status badge: text plus icon/shape; color reinforces but never carries meaning alone.
  • Feedback: inline field errors, page-level error boundary, skeleton only when layout is known, retry action for recoverable failures.
  • Rate-limited state: explain when an analytics start, stream, or manual retry is temporarily limited; preserve ready content, honor the server Retry-After, and never run a client-side retry loop.

Data visualization

  • Lead with a written takeaway and exact KPI; charts support the conclusion rather than decorate it.
  • Default comparisons: current period vs previous equivalent period and merchant vs eligible merchants in the same category and selected period. Peer cards show the merchant-weighted cohort median and the active merchant's performance percentile; each peer contributes one value regardless of transaction volume. A higher displayed percentile always means better, so processing-time and fee-rate ranks are direction-adjusted. Peer values appear only when at least 10 other merchants and 1,000 combined peer sessions contribute; otherwise show a clear insufficient-data state.
  • Always show unit, timezone/calendar, exact reporting period, comparison baseline, sample size, freshness, and missing-data caveat where material.
  • Success/failure colors remain semantic and consistent. Never use a rainbow categorical palette for ordered values.
  • Tooltips are keyboard reachable and not the sole location of important values.
  • Charts have a textual summary and downloadable/visible data table when practical.
  • Zero, unavailable, suppressed, and not-yet-calculated are visually and linguistically distinct.
  • A ready section appears immediately without layout-shifting or blocking on unfinished siblings. Overall progress remains understandable without relying on animation.
  • Source statuses Paid and Verified both count as successful bank payments because funds were deducted. Keep them visually distinguishable: Paid means merchant verification is incomplete and is an actionable gap; Verified is finalized.
  • Present fee_amount_rial as the merchant's real fee, summed once per session. Never multiply it by retry attempts or label it as obfuscated/relative-only in the product.

Motion

  • Functional motion only: drawer/dialog transitions, focus/selection feedback, and short chart/filter state changes.
  • Typical duration 120–220 ms; enter may be slightly slower than exit.
  • Prefer opacity and transform. Avoid layout-heavy animation and dashboard-wide scroll reveals.
  • Honor prefers-reduced-motion by removing nonessential movement.
  • No third-party scroll-reveal library is planned for the authenticated panel. Reconsider only for a separately scoped marketing surface.

Persian, direction, and formatting

  • Root UI is lang="fa" dir="rtl".
  • Technical identifiers, URLs, hashes, and mixed Latin values use isolated LTR spans (dir="ltr") to prevent bidi corruption.
  • Use Intl.NumberFormat/Intl.DateTimeFormat. Display merchant-facing dates in the Jalali calendar and Asia/Tehran; API instants remain ISO-8601 UTC. Specifications must still choose Persian vs Latin digits and Rial/Toman terminology before implementation.
  • Do not concatenate translated fragments. Write complete Persian strings with correct نیم‌فاصله.
  • Authentication errors must not reveal whether a username exists.

Accessibility and interaction

  • WCAG 2.2 AA target.
  • Visible :focus-visible ring on every interactive element.
  • Semantic buttons/links/forms before ARIA; icons have accessible names or are hidden when decorative.
  • All functionality is keyboard operable. Dialogs/drawers manage focus correctly.
  • Validation is associated with fields and announced; asynchronous updates use restrained live regions.
  • Loading indicators expose status text; avoid endless skeletons.
  • Destructive or consequential actions require clear confirmation and recovery where possible.

Current decisions and change log

Version Date Decision
0.2.1 2026-08-20 Defined same-category peer cards as an equal-per-merchant median plus direction-aware percentile. This prevents the dataset's largest merchants from dominating what should represent a typical peer.
0.2.0 2026-08-20 Corrected payment lifecycle semantics from CTO guidance: Paid and Verified are bank-payment successes, Paid remains unverified, and session-level fees are real Rial amounts.
0.1.9 2026-08-20 Added rate-limit UX, 15-second SSE heartbeat/recovery, one-hour replay, and failed-section-only manual retry.
0.1.8 2026-08-20 Fixed the four parallel section ids, 30-day cache, idempotent start/SSE flow, and section timeout/retry behavior.
0.1.7 2026-08-20 Moved analytics results to high-TTL Redis and defined partial, parallel section delivery through authenticated SSE.
0.1.6 2026-08-20 Replaced time-based analytics freshness with lazy first-access calculation and revision-based merchant/cohort cache states; defined Saturday–Friday weeks.
0.1.5 2026-08-20 Removed holiday/event reporting; limited period controls to daily, weekly, Jalali-month, and Persian-season views.
0.1.2 2026-08-20 Added the insufficient-data state and visibility threshold for anonymized peer comparisons.
0.1.1 2026-08-20 Fixed merchant-facing time presentation to the Jalali calendar in Asia/Tehran, with explicit UTC API instants.
0.1.0 2026-08-20 Established ZarinPal-inspired RTL foundation from supplied public/panel screenshots; fixed SvelteKit/Svelte/TypeScript/Tailwind stack; defined responsive shell, core components, accessibility, chart, and motion rules.

Any material token/component/interaction change increments this version and adds a row describing why it changed. A surprising or difficult-to-reverse change also receives an ADR.

Open design decisions before approval

  1. Licensed Yekan-family assets vs open Vazirmatn fallback.
  2. Persian or Latin digits for dashboard values.
  3. Rial-only vs Toman display and conversion wording.
  4. Exact information architecture and MVP metric set after the analytics spec.
  5. Whether a dark theme is required; it is not assumed merely because the public site uses dark feature sections.