Skip to main content

Documentation Index

Fetch the complete documentation index at: https://grantmaster.dev/llms.txt

Use this file to discover all available pages before exploring further.

Modal System

Goal

Unify all product dialogs in GrantMaster on one interaction model and one visual system.

Public Components

  • AppModal
  • AppDrawer (bottom sheet on mobile, side panel on desktop)
  • ModalHeader
  • ModalTitle
  • ModalDescription
  • ModalBody
  • ModalFooter
  • ModalNotice
  • ModalMetaList
  • ModalCheckboxField
  • ConfirmDialog
  • DecisionDialog
  • ReviewDialog
  • ActionListDialog
  • DrawerFooter
  • DrawerCloseFooter

When To Use What

  • Use ConfirmDialog for irreversible or destructive actions.
  • Use DecisionDialog for 2-3 mutually exclusive outcomes.
  • Use ReviewDialog for inspect-then-decide flows with richer body content.
  • Use ActionListDialog for user/admin action menus.
  • Use AppDrawer for detail views, settings panels, and inspect flows on mobile (bottom sheet) and desktop (side panel). Preferred over AppModal when content is read-heavy or the user needs to reference the page behind.
  • Use AppModal for standard forms and detail views that do not fit the patterns above.
  • Use legacy Modal only when migrating existing code incrementally.

Drawer Variant

The AppDrawer component renders as a bottom sheet on mobile and a side panel (right-aligned, 28rem) on desktop. It integrates with the declarative modal pipeline via variant: 'drawer' in ModalDef.
<AppDrawer open={open} onOpenChange={setOpen} title="Details">
  <p>Content here</p>
</AppDrawer>
Drawers include:
  • Drag handle on mobile (visual affordance for swipe)
  • Responsive layout: bottom sheet below lg breakpoint, side panel above
  • Same glass-frosted backdrop as AppModal
  • Automatic focus trapping and scroll locking via Radix Dialog

Sizes

  • sm: short confirmations, single-field dialogs
  • md: standard forms and install flows
  • lg: review flows and denser detail views
  • xl: large forms or multi-section content
  • 2xl: rare legacy compatibility surface
  • full: only for workflows that genuinely need near-full-viewport width

Header Rules

  • Optional icon on the left
  • Required title for product dialogs
  • Optional short description under the title
  • Close button always in the top-right
  • Primary action rightmost
  • Secondary action immediately to its left
  • Cancel when abandoning an editable or confirmable flow
  • Close when the dialog is passive or already completed
  • Destructive actions use destructive
  • Success/approve actions use success

Copy Rules

  • Prefer short verbs: Save Role, Accept, Reject, Revoke Access, Install Module
  • Avoid vague labels such as Confirm unless the action is generic
  • Keep descriptions short and operational
  • Put longer risk explanation in ModalNotice, not the title

Accessibility Rules

  • All dialogs must expose a title
  • All dialogs must expose a description or a hidden fallback description
  • ESC closes only when dismissible is true
  • Outside click closes only when dismissible is true
  • Focus returns to the trigger through Radix dialog behavior
  • Loading states must disable conflicting actions

Pattern Examples

Confirm Dialog

<ConfirmDialog
  open={open}
  onOpenChange={setOpen}
  title="Revoke Access Link"
  description="Review the details before removing access."
  tone="danger"
  confirmLabel="Revoke Access"
  onConfirm={handleRevoke}
  notice={<ModalNotice tone="danger">Access will be removed immediately.</ModalNotice>}
>
  <ModalMetaList items={items} />
</ConfirmDialog>

Review Dialog

<ReviewDialog
  open={open}
  onOpenChange={setOpen}
  title="Review Submission"
  description="Inspect the submission details and record the outcome."
  actions={[
    { key: 'reject', label: 'Reject', variant: 'destructive', onSelect: reject },
    { key: 'accept', label: 'Accept', variant: 'success', onSelect: accept },
  ]}
>
  <SubmissionSummary />
</ReviewDialog>

Action List Dialog

<ActionListDialog
  open={open}
  onOpenChange={setOpen}
  title={user.name}
  description={user.email}
  actions={[
    { key: 'change-role', label: 'Change Role', onSelect: handleChangeRole },
    { key: 'deactivate', label: 'Deactivate User', variant: 'destructive', onSelect: handleDeactivate },
  ]}
/>

Migration Rules

  • New product dialogs should import from @/components/ui/modal-system
  • Do not add new product flows directly on DialogContent
  • Do not add new confirm flows through alert-dialog or bespoke hooks
  • Keep domain logic in feature components; keep layout and interaction rules in the modal system