modals — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited modals (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
STOP. READ THIS ENTIRE FILE BEFORE CREATING MODALS.
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.
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.
| 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 | Width | Use Case |
|---|---|---|
small | 320px | Confirmations, alerts |
medium | 500px | Forms, settings (default) |
large | 720px | Complex forms, tables |
fullscreen | 95vw / 95vh | Data grids, large content |
These are the ONLY events that work with modals:
| 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 |
toolbars=[{
"position": "top",
"items": [
{"type": "button", "label": "Settings", "event": "modal:open:settings-modal"}
]
}]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>',
]
)close_on_escape=False)close_on_overlay_click=False)When reset_on_close=True (default):
<input>, <textarea>, <select> elements are reset to their initial valuesWhen reset_on_close=False:
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');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"}
]
}]
)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
)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));
}
""",
)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);
});
});
""",
)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) |
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 preferenceUse CSS variables for consistent theming:
.pywry-modal-container {
background: var(--pywry-bg-secondary);
color: var(--pywry-text-primary);
border: 1px solid var(--pywry-border);
}❌ 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')"
You can also use dict syntax if preferred:
modals=[
{
"component_id": "info-modal",
"title": "Information",
"items": ["<p>Some info here</p>"],
"size": "small",
}
]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=["..."]),
]
)~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.