Skip to content
Development
Skill

/modals

**STOP. READ THIS ENTIRE FILE BEFORE CREATING MODALS.**

From plugin
pywry
9918 skills1 agent2 commands1 MCP

Claude Code

$ npx -y skills add deeleeramone/PyWry --skill modals --agent claude-code

SKILL.md

modals.SKILL.md

Modals - MANDATORY PATTERN

> **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

1. **X Button**: Every modal has a close button in the top-right corner 2. **Escape Key**: Press Escape to close (unless `close_on_escape=False`) 3. **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="""
Read more
@deeleeramone
Ships withpywry

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.

9918 skills1 agent1 MCP
Get the whole plugin

Stars on GitHub

Momentum

stars in total
99
Star history of deeleeramone/PyWry on GitHub, a chart by star-history.com

Other skills on pywry.

pywry-orientation

pywry-orientation

When to reach for PyWry MCP tools (native webview rendering, Plotly, TradingView, AgGrid, chat) instead of writing…

@deeleeramone@deeleeramoneInstall Skill
chat_agent

chat_agent

How an agent operates inside a PyWry chat widget — reading user messages, attachments, @-context, tool-call result cards,…

@deeleeramone@deeleeramoneInstall Skill
component_reference

component_reference

**STOP. THIS IS THE AUTHORITATIVE REFERENCE FOR ALL COMPONENTS.** **YOU MUST USE THE EXACT EVENT SIGNATURES DOCUMENTED HERE.**…

@deeleeramone@deeleeramoneInstall Skill

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills10d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc11d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react1d252k
@facebook@facebookInstall Skill

Questions about modals.

Which coding agents can use modals?
modals installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.