Modals - MANDATORY PATTERN
SkillMediaModals are reusable overlay dialogs that can contain any content - forms, confirmations, information panels, or custom HTML. They follow the same pattern as toolbars, passed to .show- functions.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Modals - MANDATORY PATTERN skill
About this capability
PyWry is a cross-platform app factory, rendering engine and UI toolkit for Python that produces native desktop, web, and notebook experiences from a single API.
What this skill tells your AI
The instructions your AI receives, as published by deeleeramone/pywry in pywry/pywry/mcp/skills/modals/SKILL.md and read by ahel’s review.
STOP. READ THIS ENTIRE FILE BEFORE CREATING MODALS.
Overview
Modals are reusable overlay dialogs that can contain any content - forms, confirmations, information panels, or custom HTML. They follow the same pattern as toolbars, passed to .show-* functions.
The ONLY Correct Way to Create Modals
Copy this EXACTLY. Do not deviate.
from pywry import Modal, show_plotly
fig = go.Figure(...)
widget = show_plotly(
fig,
modals=[
Modal(
component_id="settings-modal",
title="Settings",
items=["<p>Configure your preferences here.</p>"],
size="medium",
)
],
toolbars=[{
"position": "top",
"items": [
{"type": "button", "label": "Open Settings", "event": "modal:open:settings-modal"}
]
}]
)
THAT'S IT. Button click opens the modal automatically.
Modal Schema
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
component_id | str | YES | - | Unique identifier for the modal |
title | str | no | "" | Modal header title |
items | list[str] | no | [] | HTML content strings |
size | str | no | "medium" | "small", "medium", "large", "fullscreen" |
width | str | no | None | Custom width (e.g., "600px", "80%") |
max_height | str | no | None | Custom max-height (e.g., "400px") |
overlay_opacity | float | no | 0.5 | Backdrop opacity (0.0-1.0) |
close_on_escape | bool | no | True | Close when Escape key pressed |
close_on_overlay_click | bool | no | True | Close when clicking outside modal |
reset_on_close | bool | no | True | Reset form inputs when closed |
on_close_event | str | no | None | Event to emit when modal closes |
open_on_load | bool | no | False | Open modal immediately on page load |
style | str | no | "" | Additional CSS for this modal |
script | str | no | "" | Additional JavaScript for this modal |
class_name | str | no | "" | Additional CSS classes |
Size Presets
| Size | Width | Use Case |
|---|---|---|
small | 320px | Confirmations, alerts |
medium | 500px | Forms, settings (default) |
large | 720px | Complex forms, tables |
fullscreen | 95vw / 95vh | Data grids, large content |
Modal Events
These are the ONLY events that work with modals:
Opening and Closing
| Event Type | Description |
|---|---|
modal:open:<component_id> | Opens the modal |
modal:close:<component_id> | Closes the modal |
modal:toggle:<component_id> | Toggles open/closed state |
Example: Button to Open Modal
toolbars=[{
"position": "top",
"items": [
{"type": "button", "label": "Settings", "event": "modal:open:settings-modal"}
]
}]
Example: Button Inside Modal to Close
Modal(
component_id="confirm-modal",
title="Confirm Action",
items=[
"<p>Are you sure?</p>",
'<button onclick="window.pywry.modal.close(\'confirm-modal\')">Cancel</button>',
'<button onclick="doAction()">Confirm</button>',
]
)
Built-in Close Behaviors
- X Button: Every modal has a close button in the top-right corner
- Escape Key: Press Escape to close (unless
close_on_escape=False) - Overlay Click: Click outside modal to close (unless
close_on_overlay_click=False)
reset_on_close Behavior
When reset_on_close=True (default):
- All
<input>,<textarea>,<select>elements are reset to their initial values - Form state is cleared when modal closes
- Next open shows fresh form
When reset_on_close=False:
- Form values persist between opens
- User can resume where they left off
JavaScript API
Access modal functions via window.pywry.modal:
// Open a modal
window.pywry.modal.open('my-modal');
// Close a modal
window.pywry.modal.close('my-modal');
// Toggle a modal
window.pywry.modal.toggle('my-modal');
// Check if modal is open
const isOpen = window.pywry.modal.isOpen('my-modal');
Complete Examples
1. Settings Modal with Form
from pywry import Modal, show_plotly
widget = show_plotly(
fig,
modals=[
Modal(
component_id="settings",
title="Chart Settings",
items=[
'<form id="settings-form">',
' <label>Title: <input type="text" name="title" value="My Chart"></label>',
' <label>Theme: <select name="theme">',
' <option value="dark">Dark</option>',
' <option value="light">Light</option>',
' </select></label>',
' <button type="button" onclick="applySettings()">Apply</button>',
'</form>',
],
size="medium",
reset_on_close=False, # Keep form values
)
],
toolbars=[{
"position": "top",
"items": [
{"type": "button", "label": "⚙️ Settings", "event": "modal:open:settings"}
]
}]
)
2. Confirmation Dialog
Modal(
component_id="delete-confirm",
title="Delete Item",
items=[
'<p>Are you sure you want to delete this item?</p>',
'<p style="color: var(--pywry-text-danger);">This action cannot be undone.</p>',
'<div class="pywry-modal-footer">',
' <button onclick="window.pywry.modal.close(\'delete-confirm\')">Cancel</button>',
' <button onclick="confirmDelete()" class="danger">Delete</button>',
'</div>',
],
size="small",
close_on_overlay_click=False, # Force user to click a button
)
3. Modal with Custom Styling
Modal(
component_id="custom-modal",
title="Styled Modal",
items=["<p>Custom styled content</p>"],
style="""
#custom-modal .pywry-modal-container {
border: 2px solid var(--pywry-accent);
border-radius: 16px;
}
#custom-modal .pywry-modal-header {
background: linear-gradient(90deg, var(--pywry-accent), var(--pywry-bg-secondary));
}
""",
)
4. Modal with Custom JavaScript
Modal(
component_id="data-modal",
title="Data Preview",
items=['<div id="data-preview"></div>'],
script="""
// Populate data when modal opens
document.getElementById('data-modal').addEventListener('modal:opened', () => {
fetch('/api/data')
.then(r => r.json())
.then(data => {
document.getElementById('data-preview').textContent = JSON.stringify(data, null, 2);
});
});
""",
)
CSS Selectors for Styling
Target modal elements with these selectors:
| Selector | Element |
|---|---|
#<component_id> | The modal overlay |
#<component_id> .pywry-modal-container | The modal box |
#<component_id> .pywry-modal-header | Header with title and close button |
#<component_id> .pywry-modal-title | Title text |
#<component_id> .pywry-modal-close | X close button |
#<component_id> .pywry-modal-body | Content area |
#<component_id> .pywry-modal-footer | Footer area (if you add one) |
Theme Awareness
Modals automatically inherit the current theme:
.pywry-theme-dark→ Dark background, light text.pywry-theme-light→ Light background, dark text.pywry-theme-system→ Follows OS preference
Use CSS variables for consistent theming:
.pywry-modal-container {
background: var(--pywry-bg-secondary);
color: var(--pywry-text-primary);
border: 1px solid var(--pywry-border);
}
Common Mistakes
❌ Wrong: event: "open-modal" (missing modal: prefix and component_id)
❌ Wrong: event: "modal:open" (missing component_id)
❌ Wrong: Using onclick without window.pywry.modal. prefix
✅ Correct: event: "modal:open:my-modal"
✅ Correct: onclick="window.pywry.modal.close('my-modal')"
Dict Syntax (Alternative to Modal Class)
You can also use dict syntax if preferred:
modals=[
{
"component_id": "info-modal",
"title": "Information",
"items": ["<p>Some info here</p>"],
"size": "small",
}
]
Integration with Toolbars
Modals and toolbars work together seamlessly:
show_dataframe(
df,
toolbars=[{
"position": "top",
"items": [
{"type": "button", "label": "Export", "event": "grid:export-csv"},
{"type": "button", "label": "Filter", "event": "modal:open:filter-modal"},
{"type": "button", "label": "Help", "event": "modal:open:help-modal"},
]
}],
modals=[
Modal(component_id="filter-modal", title="Filters", items=["..."]),
Modal(component_id="help-modal", title="Help", items=["..."]),
]
)
Signals
- GitHub stars
- 93
- Forks
- 6
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
modals- Source
- github.com/deeleeramone/pywry