admin/docs/consumer-ui-guidelines.md
alisaza e1eaf5eff5 feat: initial ghabilee-admin backoffice app
Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js
app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
2026-09-05 13:12:59 +03:30

6.7 KiB
Raw Blame History

Consumer UI guidelines

Hard constraints for the end-user app under frontend/app/(consumer)/ and shared consumer surfaces (components/consumer/*, PWA/auth overlays used there, event/review/booking flows).

Agents must follow the always-on Cursor rules consumer-modals, consumer-input, consumer-button, and consumer-mobile-first. This doc is the longer companion.

1. Overlays: ConsumerModal only

Any modal / dialog in the consumer app must use components/consumer/ConsumerModal.

// ❌ BAD — admin/shared Modal in consumer UI
import Modal from '@/components/modals/Modal'

// ✅ GOOD
import ConsumerModal from '@/components/consumer/ConsumerModal'

Do not use components/modals/Modal, HeroUI Drawer, or ad-hoc fixed banners for consumer dialogs. Admin stays on components/modals/Modal (see admin-ui-guidelines.md).

ConsumerModal is always a bottom sheet (phone and desktop). There is no centered / top / auto placement.

3. Form fields: ConsumerInput only

Any text field, select, textarea, OTP, radio, checkbox, switch, or number input in the consumer app must use components/consumer/ConsumerInput.

// ❌ BAD — admin/shared Input in consumer UI
import Input from '@/components/formElements/Input'

// ✅ GOOD
import ConsumerInput from '@/components/consumer/ConsumerInput'

Do not use components/formElements/Input in consumer surfaces.

Props: use size (sm | md | lg), radius (full | control, 10px), and tone (muted | bordered). muted applies #E2E2E280 fill; bordered is white surface + border. Select is always muted. Do not pass HeroUI size, radius, or variant.

Standalone mode: when the parent is not wrapped in react-hook-form, pass value and onValueChangeConsumerInput creates an internal form bridge.

Custom wrappers: pass inputWrapper to merge classes (auth full-radius fields, host contact section).

OTP styling: pass otpClassNames={{ segment, segmentWrapper }} when the default consumer OTP look is not enough (e.g. auth gate).

Temporary exception: birthDatePicker, combobox, and other admin-only field kinds not yet in ConsumerInput v1 may keep formElements/Input for that single field until support is added.

Implementation lives under components/consumer/input/ (ConsumerFieldShell, consumerInputStyles.ts, per-kind controls). Admin panel and event-create wizard stay on formElements/Input.

4. Actions: ConsumerButton only

Any button, submit control, or link-styled action in the consumer app must use components/consumer/ConsumerButton.

// ❌ BAD — admin/shared Button in consumer UI
import Button from '@/components/formElements/Button'

// ✅ GOOD
import ConsumerButton from '@/components/consumer/ConsumerButton'

Do not use components/formElements/Button in consumer surfaces.

Props: use fill (orange | navy | gray | none), size (xs | sm | md | lg | xl), radius (full | control), textColor, fontSize, align, and icon props (iconStart / iconEnd / iconOnly). Do not pass HeroUI size, radius, variant, or color.

Size tokens (button heights):

size height
xs 40px
sm 44px
md 48px
lg 52px (default text CTA)
xl 60px

Default: lg for text buttons, sm for iconOnly.

هم‌نام بودن ≠ هم‌اندازه بودن: ConsumerButton size and ConsumerInput size reuse labels (sm, md, lg) but map to different pixel heights. Do not assume a field and a button with the same token name share one height.

token ConsumerButton size ConsumerInput size
xs 40px
sm 44px 32px
md 48px 48px
lg 52px 52px
xl 60px

Four button roles (+ icon chrome):

Role API Typical use
Conversion CTA fill="orange" (default) login, book, continue
Dark primary fill="navy" modal confirm, host CTAs
Inline / card pill fill="gray" cancel booking, nav chips, follow
Text / ghost action fill="none" + textColor change mobile, resend code, links
Destructive fill="none" + textColor="text-fourth-900" delete, cancel reservation
Icon-only iconOnly + size="sm" bookmark, share, edit on cards

Dual cancel/primary rows: ConsumerActionButtons (modal footers, ConsumerFormActionBar). Do not duplicate that layout.

No ad-hoc visual className: if Figma needs a look this component cannot express via fill, size, radius, textColor, fontSize, or align, stop and ask the product owner — do not patch with bg-*, text-*, rounded-*, etc. Extend ConsumerButton only after explicit approval. Layout-only classes (flex-1, mt-5, shrink-0, …) are fine.

Admin panel stays on formElements/Button.

5. Mobile excellence; desktop does not matter

The consumer app is a mobile product.

  • Target phone widths (~360430px) first and last.
  • No horizontal scroll, clipped content, overlapping BottomNav, or ignored safe-area insets.
  • Touch targets ≥ 44px; primary CTAs reachable with one thumb when practical.
  • Fix mobile layout bugs before any other visual work.

Non-goal: desktop / wide-viewport polish. Do not invest in multi-column desktop layouts or large-screen aesthetics unless explicitly requested. A correct phone layout always wins over a polished desktop layout.

Design tokens and patterns: docs/frontend/consumer-design-system.md. Copy catalog: consumer-texts.md.

6. Event cards

Use the single medium card components/consumer/EventCard everywhere.

  • Fixed top: poster + date / time / category / location + title.
  • Variable bottom: pass page-specific UI via children (inline divs).
  • Optional helper: EventCapacityBadge for remaining-seat chips in discovery/host footers.
  • Loading state: EventCardSkeleton / EventCardSkeletonList.
  • Dense lists: CompactEventCard or ConsumerEventThumbRow when the medium card is too tall.