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
- Use sparingly - don't overuse modals
- Provide clear close affordances
- Keep content concise
- Use appropriate size for content
- Handle loading states within modal
- Prevent backdrop clicks for critical actions