Skip to content
Back to skills

087 Static 4f4f717c

ASecurity

Static image export requires Kaleido:

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 11, 2026
datapythongobashexpress

Works with

  • cli

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 087-static_4f4f717c --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 087 Static 4f4f717c?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 087 Static 4f4f717c
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-087-static-4f4f717c/badge)](https://www.skillsdirectory.com/skills/tools-only-087-static-4f4f717c)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
# Export and Interactivity

## Static Image Export

### Installation

Static image export requires Kaleido:

```bash
uv pip install kaleido
```

Kaleido v1+ requires Chrome/Chromium on your system.

### Supported Formats

- **Raster**: PNG, JPEG, WebP
- **Vector**: SVG, PDF

### Writing to File

```python
import plotly.express as px

fig = px.scatter(df, x='x', y='y')

# Format inferred from extension
fig.write_image('chart.png')
fig.write_image('chart.pdf')
fig.write_image('chart.svg')

# Explicit format
fig.write_image('chart', format='png')
```

### Converting to Bytes

```python
# Get image as bytes
img_bytes = fig.to_image(format='png')

# Display in Jupyter
from IPython.display import Image
Image(img_bytes)

# Save to file manually
with open('chart.png', 'wb') as f:
    f.write(img_bytes)
```

### Customizing Export

```python
fig.write_image(
    'chart.png',
    format='png',
    width=1200,
    height=800,
    scale=2  # Higher resolution
)
```

### Setting Export Defaults

```python
import plotly.io as pio

pio.kaleido.scope.default_format = 'png'
pio.kaleido.scope.default_width = 800
pio.kaleido.scope.default_height = 600
pio.kaleido.scope.default_scale = 2
```

### Exporting Multiple Figures

```python
import plotly.io as pio

# Kaleido v1+ only
pio.write_images(
    fig=[fig1, fig2, fig3],
    file=['chart1.png', 'chart2.png', 'chart3.png']
)
```

## Interactive HTML Export

### Basic Export

```python
# Full standalone HTML
fig.write_html('interactive_chart.html')

# Open in browser
fig.show()
```

### File Size Control

```python
# Full library embedded (~5MB file)
fig.write_html('chart.html', include_plotlyjs=True)

# CDN reference (~2KB file, requires internet)
fig.write_html('chart.html', include_plotlyjs='cdn')

# Local reference (requires plotly.min.js in same directory)
fig.write_html('chart.html', include_plotlyjs='directory')

# No library (for embedding in existing HTML with Plotly.js)
fig.write_html('chart.html', include_plotlyjs=False)
```

### HTML Configuration

```python
fig.write_html(
    'chart.html',
    config={
        'displayModeBar': True,
        'displaylogo': False,
        'toImageButtonOptions': {
            'format': 'png',
            'filename': 'custom_image',
            'height': 800,
            'width': 1200,
            'scale': 2
        }
    }
)
```

### Embedding in Templates

```python
# Get only the div (no full HTML structure)
html_div = fig.to_html(
    full_html=False,
    include_plotlyjs='cdn',
    div_id='my-plot'
)

# Use in Jinja2 template
template = """
<html>
<body>
    <h1>My Dashboard</h1>
    {{ plot_div | safe }}
</body>
</html>
"""
```

## Interactivity Features

### Built-in Interactions

Plotly figures automatically support:

- **Hover tooltips** - Display data on hover
- **Pan and zoom** - Click and drag to pan, scroll to zoom
- **Box/lasso select** - Select multiple points
- **Legend toggling** - Click to hide/show traces
- **Double-click** - Reset axes

### Hover Customization

```python
# Hover mode
fig.update_layout(
    hovermode='closest'  # 'x', 'y', 'closest', 'x unified', False
)

# Custom hover template
fig.update_traces(
    hovertemplate='<b>%{x}</b><br>' +
                  'Value: %{y:.2f}<br>' +
                  'Extra: %{customdata[0]}<br>' +
                  '<extra></extra>'
)

# Hover data in Plotly Express
fig = px.scatter(
    df, x='x', y='y',
    hover_data={
        'extra_col': True,     # Show column
        'x': ':.2f',           # Format column
        'hidden': False        # Hide column
    },
    hover_name='name_column'   # Bold title
)
```

### Click Events (Dash/FigureWidget)

For web applications, use Dash or FigureWidget for click handling:

```python
# With FigureWidget in Jupyter
import plotly.graph_objects as go

fig = go.FigureWidget(data=[go.Scatter(x=[1, 2, 3], y=[4, 5, 6])])

def on_click(trace, points, selector):
    print(f'Clicked on points: {points.point_inds}')

fig.data[0].on_click(on_click)
fig
```

### Zoom and Pan

```python
# Disable zoom/pan
fig.update_xaxes(fixedrange=True)
fig.update_yaxes(fixedrange=True)

# Set initial zoom
fig.update_xaxes(range=[0, 10])
fig.update_yaxes(range=[0, 100])

# Constrain zoom
fig.update_xaxes(
    range=[0, 10],
    constrain='domain'
)
```

### Rangeslider (Time Series)

```python
fig = px.line(df, x='date', y='value')

# Add rangeslider
fig.update_xaxes(rangeslider_visible=True)

# Customize rangeslider
fig.update_xaxes(
    rangeslider=dict(
        visible=True,
        thickness=0.05,
        bgcolor='lightgray'
    )
)
```

### Range Selector Buttons

```python
fig.update_xaxes(
    rangeselector=dict(
        buttons=list([
            dict(count=1, label='1m', step='month', stepmode='backward'),
            dict(count=6, label='6m', step='month', stepmode='backward'),
            dict(count=1, label='YTD', step='year', stepmode='todate'),
            dict(count=1, label='1y', step='year', stepmode='backward'),
            dict(step='all', label='All')
        ]),
        x=0.0,
        y=1.0,
        xanchor='left',
        yanchor='top'
    )
)
```

### Buttons and Dropdowns

```python
fig.update_layout(
    updatemenus=[
        dict(
            type='buttons',
            direction='left',
            buttons=list([
                dict(
                    args=[{'type': 'scatter'}],
                    label='Scatter',
                    method='restyle'
                ),
                dict(
                    args=[{'type': 'bar'}],
                    label='Bar',
                    method='restyle'
                )
            ]),
            x=0.1,
            y=1.15
        )
    ]
)
```

### Sliders

```python
fig.update_layout(
    sliders=[
        dict(
            active=0,
            steps=[
                dict(
                    method='update',
                    args=[{'visible': [True, False]},
                          {'title': 'Dataset 1'}],
                    label='Dataset 1'
                ),
                dict(
                    method='update',
                    args=[{'visible': [False, True]},
                          {'title': 'Dataset 2'}],
                    label='Dataset 2'
                )
            ],
            x=0.1,
            y=0,
            len=0.9
        )
    ]
)
```

## Animations

### Using Plotly Express

```python
fig = px.scatter(
    df, x='gdp', y='life_exp',
    animation_frame='year',     # Animate over this column
    animation_group='country',  # Group animated elements
    size='population',
    color='continent',
    hover_name='country',
    log_x=True,
    range_x=[100, 100000],
    range_y=[25, 90]
)

# Customize animation speed
fig.layout.updatemenus[0].buttons[0].args[1]['frame']['duration'] = 1000
fig.layout.updatemenus[0].buttons[0].args[1]['transition']['duration'] = 500
```

### Using Graph Objects

```python
import plotly.graph_objects as go

fig = go.Figure(
    data=[go.Scatter(x=[1, 2], y=[1, 2])],
    layout=go.Layout(
        updatemenus=[dict(
            type='buttons',
            buttons=[dict(label='Play',
                         method='animate',
                         args=[None])]
        )]
    ),
    frames=[
        go.Frame(data=[go.Scatter(x=[1, 2], y=[1, 2])]),
        go.Frame(data=[go.Scatter(x=[1, 2], y=[2, 3])]),
        go.Frame(data=[go.Scatter(x=[1, 2], y=[3, 4])])
    ]
)
```

## Displaying Figures

### In Jupyter

```python
# Default renderer
fig.show()

# Specific renderer
fig.show(renderer='notebook')  # or 'jupyterlab', 'colab', 'kaggle'
```

### In Web Browser

```python
fig.show()  # Opens in default browser
```

### In Dash Applications

```python
import dash
from dash import dcc, html
import plotly.express as px

app = dash.Dash(__name__)

fig = px.scatter(df, x='x', y='y')

app.layout = html.Div([
    dcc.Graph(figure=fig)
])

app.run_server(debug=True)
```

### Saving and Loading

```python
# Save as JSON
fig.write_json('figure.json')

# Load from JSON
import plotly.io as pio
fig = pio.read_json('figure.json')

# Save as HTML
fig.write_html('figure.html')
```

## Configuration Options

### Display Config

```python
config = {
    'displayModeBar': True,      # Show toolbar
    'displaylogo': False,        # Hide Plotly logo
    'modeBarButtonsToRemove': ['pan2d', 'lasso2d'],  # Remove buttons
    'toImageButtonOptions': {
        'format': 'png',
        'filename': 'custom_image',
        'height': 500,
        'width': 700,
        'scale': 1
    },
    'scrollZoom': True,          # Enable scroll zoom
    'editable': True,            # Enable editing
    'responsive': True           # Responsive sizing
}

fig.show(config=config)
fig.write_html('chart.html', config=config)
```

### Available Config Options

- `displayModeBar`: Show/hide toolbar ('hover', True, False)
- `displaylogo`: Show Plotly logo
- `modeBarButtonsToRemove`: List of buttons to hide
- `modeBarButtonsToAdd`: Custom buttons
- `scrollZoom`: Enable scroll to zoom
- `doubleClick`: Double-click behavior ('reset', 'autosize', 'reset+autosize', False)
- `showAxisDragHandles`: Show axis drag handles
- `editable`: Allow editing
- `responsive`: Responsive sizing

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.