MyCuppa

Modal

Dialog overlay component for focused user interactions

Platforms:
webiosandroid
Version 1.0.0

Modal

A dialog overlay component that displays content on top of the main interface, requiring user interaction.

Features

  • Overlay backdrop with dimming
  • Customizable sizes (sm, md, lg, full)
  • Header, body, footer sections
  • Close button and escape key support
  • Scroll lock on body
  • Animations (slide, fade, scale)
  • Focus trap for accessibility

Usage

Web (React)

import { Modal, ModalHeader, ModalBody, ModalFooter } from '@cuppa/ui'

function MyComponent() {
  const [isOpen, setIsOpen] = useState(false)

  return (
    <>
      <Button onClick={() => setIsOpen(true)}>Open Modal</Button>

      <Modal
        isOpen={isOpen}
        onClose={() => setIsOpen(false)}
        size="md"
      >
        <ModalHeader>
          <h2>Modal Title</h2>
        </ModalHeader>
        <ModalBody>
          <p>Modal content goes here...</p>
        </ModalBody>
        <ModalFooter>
          <Button variant="secondary" onClick={() => setIsOpen(false)}>
            Cancel
          </Button>
          <Button variant="primary" onClick={handleConfirm}>
            Confirm
          </Button>
        </ModalFooter>
      </Modal>
    </>
  )
}

iOS (SwiftUI)

import CuppaUI

struct MyView: View {
  @State private var showModal = false

  var body: some View {
    VStack {
      Button("Open Modal") {
        showModal = true
      }
    }
    .cuppaModal(isPresented: $showModal) {
      VStack(spacing: 20) {
        Text("Modal Title")
          .font(.headline)

        Text("Modal content goes here...")
          .font(.body)

        HStack {
          CuppaButton("Cancel", variant: .secondary) {
            showModal = false
          }
          CuppaButton("Confirm", variant: .primary) {
            handleConfirm()
            showModal = false
          }
        }
      }
      .padding()
    }
  }
}

Android (Jetpack Compose)

import com.cuppa.ui.components.CuppaModal
import androidx.compose.runtime.*

@Composable
fun MyScreen() {
  var showModal by remember { mutableStateOf(false) }

  Column {
    CuppaButton("Open Modal") {
      showModal = true
    }
  }

  if (showModal) {
    CuppaModal(
      onDismiss = { showModal = false },
      title = "Modal Title"
    ) {
      Column(
        modifier = Modifier.padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(16.dp)
      ) {
        Text("Modal content goes here...")

        Row(
          horizontalArrangement = Arrangement.spacedBy(8.dp)
        ) {
          CuppaButton("Cancel", variant = ButtonVariant.Secondary) {
            showModal = false
          }
          CuppaButton("Confirm", variant = ButtonVariant.Primary) {
            handleConfirm()
            showModal = false
          }
        }
      }
    }
  }
}

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | isOpen | boolean | false | Controls modal visibility | | onClose | function | - | Callback when modal should close | | size | string | 'md' | Modal size (sm, md, lg, full) | | closeOnBackdrop | boolean | true | Close when clicking backdrop | | closeOnEscape | boolean | true | Close on ESC key press | | showCloseButton | boolean | true | Show close button in header | | animation | string | 'fade' | Animation type (fade, slide, scale) |

Sizes

Small (sm)

Max width: 400px - For simple confirmations

Medium (md)

Max width: 600px - Default size for most use cases

Large (lg)

Max width: 900px - For detailed forms or content

Full

Full screen - For immersive experiences

Examples

Confirmation Dialog

<Modal isOpen={isOpen} onClose={onClose} size="sm">
  <ModalHeader>Confirm Action</ModalHeader>
  <ModalBody>
    Are you sure you want to delete this item?
  </ModalBody>
  <ModalFooter>
    <Button variant="secondary" onClick={onClose}>Cancel</Button>
    <Button variant="danger" onClick={handleDelete}>Delete</Button>
  </ModalFooter>
</Modal>

Form Modal

<Modal isOpen={isOpen} onClose={onClose} size="md">
  <ModalHeader>Edit Profile</ModalHeader>
  <ModalBody>
    <form>
      <Input label="Name" value={name} onChange={setName} />
      <Input label="Email" type="email" value={email} onChange={setEmail} />
    </form>
  </ModalBody>
  <ModalFooter>
    <Button variant="secondary" onClick={onClose}>Cancel</Button>
    <Button variant="primary" onClick={handleSave}>Save Changes</Button>
  </ModalFooter>
</Modal>

Accessibility

  • Focus is trapped within modal
  • First focusable element receives focus on open
  • ESC key closes modal
  • Screen reader announces modal role and label
  • Background content marked as inert

Best Practices

  1. Use sparingly - don't overuse modals
  2. Provide clear close affordances
  3. Keep content concise
  4. Use appropriate size for content
  5. Handle loading states within modal
  6. Prevent backdrop clicks for critical actions