174 lines
13 KiB
Markdown
174 lines
13 KiB
Markdown
# Design System — ZarinPal Merchant Analytics
|
||
|
||
- Status: Draft baseline for approval
|
||
- Version: 0.2.4
|
||
- 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.
|
||
- First load selects the latest complete Jalali month containing data and compares only with the immediately previous equivalent period. An absent baseline has an explicit unavailable state.
|
||
- Overview leads with bank-paid amount, bank-paid session count, success rate, and verification completion. Retry, timing, and fee modules follow in that order of detail.
|
||
- Drill-down tables for unverified-paid, failed, and retried sessions are read-only. They never reveal payer tokens or peer rows; `NoAttempt` and `InBank` are stage labels, not invented failure causes.
|
||
|
||
## 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.4 | 2026-08-20 | Fixed the static-demo default period, comparison fallback, overview KPI priority, and safe read-only drill-down scope; added accurate `NoAttempt`/`InBank` stage language from the CTO data guide. |
|
||
| 0.2.3 | 2026-08-20 | Kept authentication UX intentionally demo-sized: generic and throttled username/password login now, with a clean replacement boundary for ZarinPal SSO after acceptance. |
|
||
| 0.2.2 | 2026-08-20 | Defined backend-owned session UX: authentication tokens remain inaccessible to the frontend, and expiry/revocation returns the user to login with a generic, accessible message. |
|
||
| 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. Whether a dark theme is required; it is not assumed merely because the public site uses dark feature sections.
|