**Site index:** [https://2plot.dev/llms.txt](https://2plot.dev/llms.txt) — every page on this site, as Markdown.  
**Network index:** [https://2plot.ai/llms.txt](https://2plot.ai/llms.txt) — The 2plot network; start here to discover sibling sites.  
**Sibling sites:** 11 more in The 2plot network — listed in the site index above.  
**Sitemap:** https://2plot.dev/sitemap.xml  

> **Full documentation:** [https://muicharts.2plot.dev](https://muicharts.2plot.dev) — the dedicated dash-mui-charts documentation site, with the complete API reference and deeper examples. This page is the quick-start overview.



# MUI Charts

`dash-mui-charts` brings [MUI X](https://mui.com/x/react-charts/) to Plotly Dash — **13 components** as of 1.4.0: nine chart types, three tree views and a TimeClock, with an optional Pro tier for zoom, pan, brush and heatmaps.

This page is the catalogue summary with a taste of the charts; the full interactive documentation — 40+ pages with ~250 live examples covering every component and prop — lives at [muicharts.2plot.dev](https://muicharts.2plot.dev).

| Component | License | Key Features |
|:----------|:--------|:-------------|
| **LineChart** | Free + Pro | Line/area, stacking, zoom/pan (Pro), brush (Pro), reference lines, dateFormat (1.1.0) |
| **BarChart** | Free + Pro | Vertical/horizontal, stacked/normalized/diverging offsets, dataset mode, zoom + toolbar (Pro) |
| **CandlestickChart** | Free | OHLC candles, volume overlay, support/resistance reference lines, click events |
| **PieChart** | Free | Pie, donut, nested/concentric, gauge-arc, arc labels |
| **ScatterChart** | Free | Multi-series, z-axis color mapping, voronoi interaction |
| **CompositeChart** | Free + Pro | Layer scatter + line, zoom/pan (Pro), biaxial axes |
| **Heatmap** | Pro | Matrix visualization, continuous/piecewise color scales |
| **SparklineChart** | Free | Compact inline charts for KPI cards and tables |
| **LiveTradingChart** | Free + Pro | Real-time OHLCV candlestick streaming, volume, forecast, alerts |
| **TreeView** | Free | Data-driven items, expansion/selection control, checkbox propagation, inline label editing |
| **SimpleTreeView** | Free | Declarative tree built from children — dogfooded as the docs site's own sidebar |
| **TreeViewPro** | Pro | Kebab submenus, dividers and per-node menus (1.4.0) |
| **TimeClock** | Free | Clock-face time picking, controlled/uncontrolled, 12h/24h, pairs with DMC time inputs |

## Installation

[Visit GitHub Repo](https://github.com/pip-install-python/dash-mui-charts) · [PyPI](https://pypi.org/project/dash-mui-charts/)

```bash
pip install dash-mui-charts
```

For Pro features, set your license key in `.env`:
```
MUI_PRO_API_KEY=your-license-key-here
```

---

## Line Charts



```python
# File: docs/dash_mui_charts/line_basic.py

import os
import random
import math
import dash_mantine_components as dmc
from dash_mui_charts import LineChart

MUI_KEY = os.getenv('MUI_PRO_API_KEY', '')
random.seed(42)
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
revenue = [random.randint(30, 90) for _ in months]
expenses = [random.randint(20, 65) for _ in months]
profit = [r - e for r, e in zip(revenue, expenses)]
temp_data = [round(18 + 12 * math.sin(2 * math.pi * i / 12 - math.pi / 2) + random.uniform(-2, 2), 1) for i in range(12)]

# Extra datasets
random.seed(77)
organic = [random.randint(10, 30) for _ in months]
paid = [random.randint(8, 25) for _ in months]
referral = [random.randint(5, 15) for _ in months]

# Curve comparison data
random.seed(11)
curve_data = [random.randint(20, 80) for _ in range(8)]
curve_labels = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun', 'Mon']

GLASS = {
    "background": "light-dark(rgba(255,255,255,0.55), rgba(30,30,30,0.55))",
    "backdropFilter": "blur(16px) saturate(1.8)",
    "WebkitBackdropFilter": "blur(16px) saturate(1.8)",
    "border": "1px solid light-dark(rgba(255,255,255,0.5), rgba(255,255,255,0.08))",
}

component = dmc.Stack([
    dmc.Text("Line Charts", fw=700, size="xl"),
    dmc.Text("Versatile line and area charts with stacking, curves, reference lines, and biaxial axes.", size="sm", c="dimmed"),

    # --- Multi-Series ---
    dmc.Paper(
        dmc.Stack([
            dmc.Text("Multi-Series Line Chart", fw=600),
            dmc.Text("Revenue vs expenses with profit area fill.", size="sm", c="dimmed"),
            LineChart(
                id="mc-line-basic",
                licenseKey=MUI_KEY,
                series=[
                    {'data': revenue, 'label': 'Revenue ($k)', 'color': '#1976d2', 'curve': 'monotoneX', 'showMark': True},
                    {'data': expenses, 'label': 'Expenses ($k)', 'color': '#f57c00', 'curve': 'monotoneX', 'showMark': True},
                    {'data': profit, 'label': 'Profit ($k)', 'color': '#388e3c', 'curve': 'monotoneX', 'area': True},
                ],
                xAxis=[{'data': months, 'scaleType': 'point', 'label': 'Month'}],
                yAxis=[{'label': 'Amount ($k)'}],
                grid={'horizontal': True, 'vertical': False},
                height=350,
            ),
        ], gap="sm"),
        p="lg", radius="md", style=GLASS,
    ),

    # --- Stacked Area ---
    dmc.Paper(
        dmc.Stack([
            dmc.Text("Stacked Area Chart", fw=600),
            dmc.Text("Stacked areas showing cumulative traffic sources.", size="sm", c="dimmed"),
            LineChart(
                id="mc-line-stacked",
                licenseKey=MUI_KEY,
                series=[
                    {'data': organic, 'label': 'Organic', 'color': '#66bb6a', 'area': True, 'stack': 'traffic', 'curve': 'monotoneX'},
                    {'data': paid, 'label': 'Paid', 'color': '#42a5f5', 'area': True, 'stack': 'traffic', 'curve': 'monotoneX'},
                    {'data': referral, 'label': 'Referral', 'color': '#ab47bc', 'area': True, 'stack': 'traffic', 'curve': 'monotoneX'},
                ],
                xAxis=[{'data': months, 'scaleType': 'point'}],
                yAxis=[{'label': 'Visitors (k)'}],
                grid={'horizontal': True},
                height=300,
            ),
        ], gap="sm"),
        p="lg", radius="md", style=GLASS,
    ),

    # --- Curve Interpolation Comparison ---
    dmc.Paper(
        dmc.Stack([
            dmc.Text("Curve Interpolation", fw=600),
            dmc.Text("Same data rendered with different curve algorithms.", size="sm", c="dimmed"),
            dmc.SimpleGrid(cols={"base": 1, "md": 2}, children=[
                dmc.Stack([
                    dmc.Badge("linear", color="blue", variant="light", size="sm"),
                    LineChart(
                        id="mc-line-curve-linear",
                        licenseKey=MUI_KEY,
                        series=[{'data': curve_data, 'label': 'Value', 'color': '#1976d2', 'curve': 'linear', 'showMark': True}],
                        xAxis=[{'data': curve_labels, 'scaleType': 'point'}],
                        height=180, grid={'horizontal': True},
                    ),
                ], gap=4),
                dmc.Stack([
                    dmc.Badge("monotoneX", color="green", variant="light", size="sm"),
                    LineChart(
                        id="mc-line-curve-mono",
                        licenseKey=MUI_KEY,
                        series=[{'data': curve_data, 'label': 'Value', 'color': '#388e3c', 'curve': 'monotoneX', 'showMark': True}],
                        xAxis=[{'data': curve_labels, 'scaleType': 'point'}],
                        height=180, grid={'horizontal': True},
                    ),
                ], gap=4),
                dmc.Stack([
                    dmc.Badge("natural", color="orange", variant="light", size="sm"),
                    LineChart(
                        id="mc-line-curve-natural",
                        licenseKey=MUI_KEY,
                        series=[{'data': curve_data, 'label': 'Value', 'color': '#f57c00', 'curve': 'natural', 'showMark': True}],
                        xAxis=[{'data': curve_labels, 'scaleType': 'point'}],
                        height=180, grid={'horizontal': True},
                    ),
                ], gap=4),
                dmc.Stack([
                    dmc.Badge("step", color="violet", variant="light", size="sm"),
                    LineChart(
                        id="mc-line-curve-step",
                        licenseKey=MUI_KEY,
                        series=[{'data': curve_data, 'label': 'Value', 'color': '#7b1fa2', 'curve': 'step', 'showMark': True}],
                        xAxis=[{'data': curve_labels, 'scaleType': 'point'}],
                        height=180, grid={'horizontal': True},
                    ),
                ], gap=4),
            ]),
        ], gap="sm"),
        p="lg", radius="md", style=GLASS,
    ),

    # --- Reference Lines ---
    dmc.Paper(
        dmc.Stack([
            dmc.Text("Reference Lines", fw=600),
            dmc.Text("Temperature curve with comfort zone boundaries and a vertical launch marker.", size="sm", c="dimmed"),
            LineChart(
                id="mc-line-ref",
                licenseKey=MUI_KEY,
                series=[
                    {'data': temp_data, 'label': 'Temperature (°C)', 'color': '#ef5350', 'curve': 'natural', 'area': True},
                ],
                xAxis=[{'data': months, 'scaleType': 'point', 'label': 'Month'}],
                yAxis=[{'label': '°C', 'min': 0, 'max': 40}],
                referenceLines=[
                    {'y': 25, 'label': 'Upper comfort', 'lineStyle': {'stroke': '#ff9800', 'strokeDasharray': '5 3'}},
                    {'y': 18, 'label': 'Lower comfort', 'lineStyle': {'stroke': '#2196f3', 'strokeDasharray': '5 3'}},
                    {'x': 'Jun', 'label': 'Summer', 'lineStyle': {'stroke': '#9e9e9e', 'strokeDasharray': '3 3'}},
                ],
                grid={'horizontal': True},
                height=320,
            ),
        ], gap="sm"),
        p="lg", radius="md", style=GLASS,
    ),

    # --- Biaxial ---
    dmc.Paper(
        dmc.Stack([
            dmc.Text("Biaxial (Dual Y-Axis)", fw=600),
            dmc.Text("Revenue on the left axis, growth rate on the right. Independent scales for different units.", size="sm", c="dimmed"),
            LineChart(
                id="mc-line-biaxial",
                licenseKey=MUI_KEY,
                series=[
                    {'data': revenue, 'label': 'Revenue ($k)', 'color': '#1976d2', 'curve': 'monotoneX', 'showMark': True, 'yAxisId': 'left'},
                    {'data': [round(random.uniform(-5, 20), 1) for _ in months], 'label': 'Growth (%)', 'color': '#ef5350', 'curve': 'monotoneX', 'area': True, 'yAxisId': 'right'},
                ],
                xAxis=[{'data': months, 'scaleType': 'point'}],
                yAxis=[
                    {'id': 'left', 'label': 'Revenue ($k)', 'position': 'left'},
                    {'id': 'right', 'label': 'Growth (%)', 'position': 'right'},
                ],
                grid={'horizontal': True},
                height=320,
            ),
        ], gap="sm"),
        p="lg", radius="md", style=GLASS,
    ),
], gap="lg")
```


---

## Line Charts — Pro Features



```python
# File: docs/dash_mui_charts/line_pro.py

import os
import random
import math
from datetime import datetime, timedelta
import dash_mantine_components as dmc
from dash_mui_charts import LineChart

MUI_KEY = os.getenv('MUI_PRO_API_KEY', '')
random.seed(99)

# --- Signal vs Noise (100 points) ---
n = 100
x_vals = list(range(n))
signal = [round(50 + 20 * math.sin(2 * math.pi * i / 25) + random.gauss(0, 5), 1) for i in range(n)]
noise = [round(50 + random.gauss(0, 12), 1) for i in range(n)]

# --- Simulated 2-month hourly sensor data for slider preview ---
random.seed(42)
start_date = datetime(2025, 1, 1)
hours = 24 * 45  # 45 days
timestamps = []
temperature = []
humidity = []
for h in range(hours):
    dt = start_date + timedelta(hours=h)
    timestamps.append(int(dt.timestamp() * 1000))
    day_of_year = dt.timetuple().tm_yday
    hour = dt.hour
    seasonal = 2.0 + 8.0 * math.sin(2 * math.pi * (day_of_year - 30) / 365)
    daily_cycle = 4.0 * math.sin(2 * math.pi * (hour - 6) / 24)
    temp = seasonal + daily_cycle + random.gauss(0, 1.2)
    temperature.append(round(temp, 1))
    hum_base = 65 - 1.5 * (temp - 5)
    humidity.append(round(max(20, min(95, hum_base + random.gauss(0, 3))), 1))

# --- Brush data ---
random.seed(55)
brush_data = [random.randint(10, 90) for _ in range(50)]

GLASS = {
    "background": "light-dark(rgba(255,255,255,0.55), rgba(30,30,30,0.55))",
    "backdropFilter": "blur(16px) saturate(1.8)",
    "WebkitBackdropFilter": "blur(16px) saturate(1.8)",
    "border": "1px solid light-dark(rgba(255,255,255,0.5), rgba(255,255,255,0.08))",
}

component = dmc.Stack([
    dmc.Text("Line Charts — Pro Features", fw=700, size="xl"),
    dmc.Badge("Requires MUI Pro License", color="violet", variant="light", size="sm"),

    # --- Zoom + Pan + Slider ---
    dmc.Paper(
        dmc.Stack([
            dmc.Text("Zoom, Pan & Toolbar", fw=600),
            dmc.Text("Scroll to zoom, drag to pan. Toolbar provides zoom in/out and export. 100 data points.", size="sm", c="dimmed"),
            LineChart(
                id="mc-line-zoom",
                licenseKey=MUI_KEY,
                series=[
                    {'data': signal, 'label': 'Signal', 'color': '#1976d2', 'curve': 'monotoneX'},
                    {'data': noise, 'label': 'Noise', 'color': '#ef5350', 'curve': 'linear', 'showMark': False},
                ],
                xAxis=[{
                    'id': 'x-axis', 'data': x_vals, 'scaleType': 'linear', 'label': 'Sample Index',
                    'zoom': {'minSpan': 10, 'maxSpan': 100, 'panning': True},
                }],
                yAxis=[{'label': 'Value', 'min': 0, 'max': 100}],
                initialZoom=[{'axisId': 'x-axis', 'start': 0, 'end': 40}],
                showSlider=True,
                showToolbar=True,
                grid={'horizontal': True},
                height=400,
            ),
        ], gap="sm"),
        p="lg", radius="md", style=GLASS,
    ),

    # --- Zoom Slider with Preview (Time Series) ---
    dmc.Paper(
        dmc.Stack([
            dmc.Text("Zoom Slider with Preview", fw=600),
            dmc.Group([
                dmc.Text("45 days of simulated hourly sensor data. The slider shows a miniature preview of the full dataset.", size="sm", c="dimmed"),
                dmc.Badge("slider.preview", color="teal", variant="light", size="xs"),
            ]),
            LineChart(
                id="mc-line-slider-preview",
                licenseKey=MUI_KEY,
                height=420,
                series=[{
                    'id': 'temperature',
                    'data': temperature,
                    'label': 'Temperature (°C)',
                    'color': '#ef5350',
                    'showMark': False,
                    'area': True,
                    'curve': 'natural',
                }],
                xAxis=[{
                    'id': 'time-axis',
                    'data': timestamps,
                    'scaleType': 'time',
                    'label': 'Date',
                    'tickMinStep': 3600 * 1000 * 24,
                    'tickLabelStyle': {'angle': 35, 'fontSize': 11, 'textAnchor': 'start'},
                    'height': 50,
                    'zoom': {
                        'minSpan': 2,
                        'panning': True,
                        'filterMode': 'discard',
                        'slider': {'enabled': True, 'preview': True},
                    },
                }],
                yAxis=[{'label': '°C', 'width': 55, 'domainLimit': 'nice'}],
                grid={'horizontal': True},
                margin={'left': 65, 'right': 20, 'top': 20, 'bottom': 70},
                initialZoom=[{'axisId': 'time-axis', 'start': 0, 'end': 25}],
            ),
        ], gap="sm"),
        p="lg", radius="md", style=GLASS,
    ),

    # --- Biaxial with Slider Preview ---
    dmc.Paper(
        dmc.Stack([
            dmc.Text("Biaxial Chart with Slider Preview", fw=600),
            dmc.Text("Temperature and humidity on dual axes. filterMode='discard' auto-adjusts y-axis to visible range.", size="sm", c="dimmed"),
            LineChart(
                id="mc-line-biaxial-preview",
                licenseKey=MUI_KEY,
                height=420,
                series=[
                    {'id': 'temp', 'data': temperature, 'label': 'Temperature (°C)', 'color': '#ef5350', 'showMark': False, 'curve': 'natural', 'yAxisId': 'temp-axis'},
                    {'id': 'hum', 'data': humidity, 'label': 'Humidity (%)', 'color': '#42a5f5', 'showMark': False, 'curve': 'natural', 'yAxisId': 'hum-axis'},
                ],
                xAxis=[{
                    'id': 'biaxial-time',
                    'data': timestamps,
                    'scaleType': 'time',
                    'tickMinStep': 3600 * 1000 * 24,
                    'tickLabelStyle': {'angle': 35, 'fontSize': 11, 'textAnchor': 'start'},
                    'height': 50,
                    'zoom': {
                        'minSpan': 2, 'panning': True, 'filterMode': 'discard',
                        'slider': {'enabled': True, 'preview': True},
                    },
                }],
                yAxis=[
                    {'id': 'temp-axis', 'label': '°C', 'position': 'left', 'width': 50, 'domainLimit': 'nice', 'labelStyle': {'fill': '#ef5350'}},
                    {'id': 'hum-axis', 'label': '%', 'position': 'right', 'width': 50, 'domainLimit': 'nice', 'labelStyle': {'fill': '#42a5f5'}},
                ],
                grid={'horizontal': True},
                margin={'left': 60, 'right': 60, 'top': 20, 'bottom': 70},
                initialZoom=[{'axisId': 'biaxial-time', 'start': 30, 'end': 60}],
            ),
        ], gap="sm"),
        p="lg", radius="md", style=GLASS,
    ),

    # --- Brush Selection ---
    dmc.Paper(
        dmc.Stack([
            dmc.Text("Brush Selection", fw=600),
            dmc.Text("Click and drag to select a range. The brush overlay shows selected values.", size="sm", c="dimmed"),
            LineChart(
                id="mc-line-brush",
                licenseKey=MUI_KEY,
                series=[
                    {'data': brush_data, 'label': 'Metric A', 'color': '#7b1fa2', 'curve': 'monotoneX'},
                ],
                xAxis=[{'data': list(range(50)), 'scaleType': 'linear', 'label': 'Day'}],
                brushConfig={'enabled': True, 'preventTooltip': True},
                brushOverlay='values',
                grid={'horizontal': True},
                height=300,
            ),
        ], gap="sm"),
        p="lg", radius="md", style=GLASS,
    ),

    # --- Highlighting ---
    dmc.Paper(
        dmc.Stack([
            dmc.Text("Item & Axis Highlighting", fw=600),
            dmc.Text("Hover to see per-series highlight scoping — the active series stays vivid while others fade.", size="sm", c="dimmed"),
            LineChart(
                id="mc-line-highlight",
                licenseKey=MUI_KEY,
                series=[
                    {'data': [random.randint(100, 250) for _ in range(12)], 'label': 'Sales 2023', 'color': '#1976d2', 'curve': 'monotoneX', 'showMark': True,
                     'highlightScope': {'highlight': 'series', 'fade': 'global'}},
                    {'data': [random.randint(120, 280) for _ in range(12)], 'label': 'Sales 2024', 'color': '#388e3c', 'curve': 'monotoneX', 'showMark': True,
                     'highlightScope': {'highlight': 'series', 'fade': 'global'}},
                    {'data': [random.randint(80, 200) for _ in range(12)], 'label': 'Sales 2025', 'color': '#f57c00', 'curve': 'monotoneX', 'showMark': True,
                     'highlightScope': {'highlight': 'series', 'fade': 'global'}},
                ],
                xAxis=[{'data': ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], 'scaleType': 'point'}],
                yAxis=[{'label': 'Revenue ($k)'}],
                axisHighlight={'x': 'band', 'y': 'line'},
                grid={'horizontal': True},
                height=350,
            ),
        ], gap="sm"),
        p="lg", radius="md", style=GLASS,
    ),
], gap="lg")
```


---

## Tick Configuration & Date Formatting

New in **1.1.0** — `dateFormat` and `dateTickFormat` let you format time-scale axes directly from Python without writing JavaScript. Use `scaleType: 'time'` with epoch-ms timestamps, control tick density with `tickMinStep`, and rotate labels with `tickLabelStyle`.

| Prop | Description |
|:-----|:------------|
| `dateFormat` | Format string for **tooltip** labels (e.g. `"MMM d, YYYY"`) |
| `dateTickFormat` | Format string for **axis tick** labels (e.g. `"M/d"`) |
| `tickMinStep` | Minimum ms between ticks — use `86400000 * 7` for weekly ticks |
| `tickNumber` | Approximate target tick count (D3 rounds for readability) |
| `tickLabelStyle` | `{angle, fontSize, textAnchor}` for rotated labels |

**Format tokens:** `YYYY` (2025), `MMM` (Jan), `MM` (01), `M` (1), `dd` (01), `d` (1), `HH` (00–23), `mm` (00–59)



```python
# File: docs/dash_mui_charts/tick_hover.py

"""Tick Configuration & Date Formatting — v1.1.0 dateFormat/dateTickFormat."""
import os
import math
import random
import json
from datetime import datetime, timedelta

import dash_mantine_components as dmc
from dash import html, callback, Input, Output
from dash_mui_charts import LineChart

MUI_KEY = os.getenv('MUI_PRO_API_KEY', '')

GLASS = {
    "background": "light-dark(rgba(255,255,255,0.55), rgba(30,30,30,0.55))",
    "backdropFilter": "blur(16px) saturate(1.8)",
    "WebkitBackdropFilter": "blur(16px) saturate(1.8)",
    "border": "1px solid light-dark(rgba(255,255,255,0.5), rgba(255,255,255,0.08))",
}

# ---------------------------------------------------------------------------
# Data — 90-day sensor readings, two sensors
# ---------------------------------------------------------------------------
random.seed(42)

def _make_sensor(start, n, base, noise, trend):
    vals = []
    for i in range(n):
        vals.append(round(base + trend * i + noise * math.sin(i * 0.3) + random.gauss(0, noise * 0.3), 1))
    return vals

_start = datetime(2025, 1, 1)
_n = 90

# Epoch-ms timestamps — required for scaleType: 'time'
timestamps = [
    int((_start + timedelta(days=i)).timestamp()) * 1000
    for i in range(_n)
]

sensor_a = _make_sensor(_start, _n, base=68, noise=5,   trend=0.04)
sensor_b = _make_sensor(_start, _n, base=55, noise=3.5, trend=0.02)

WARNING_THRESHOLD = 78
# Day-45 event marker (epoch-ms)
event_ts = int((_start + timedelta(days=45)).timestamp()) * 1000


# ---------------------------------------------------------------------------
# Chart 1 — dateFormat / dateTickFormat (v1.1.0) + reference lines
# ---------------------------------------------------------------------------
chart_time_scale = dmc.Paper(dmc.Stack([
    dmc.Group([
        dmc.Text("Time-Scale Axis (v1.1.0 dateFormat)", fw=600),
        dmc.Badge("1.1.0", color="teal", variant="light", size="sm"),
    ]),
    dmc.Text(
        "Use scaleType: 'time' with epoch-ms timestamps. "
        "dateFormat controls the tooltip label; dateTickFormat controls axis tick labels. "
        "tickMinStep limits density; tickLabelStyle.angle rotates labels for readability.",
        size="sm", c="dimmed",
    ),
    LineChart(
        id="mc-th-time",
        licenseKey=MUI_KEY,
        height=340,
        series=[
            {
                "data":  sensor_a,
                "label": "Sensor A (°F)",
                "color": "#1976d2",
                "curve": "monotoneX",
                "showMark": False,
                "highlightScope": {"highlight": "series", "fade": "global"},
            },
            {
                "data":  sensor_b,
                "label": "Sensor B (°F)",
                "color": "#7b1fa2",
                "curve": "monotoneX",
                "showMark": False,
                "highlightScope": {"highlight": "series", "fade": "global"},
            },
        ],
        xAxis=[{
            "id":        "mc-th-x",
            "data":      timestamps,
            "scaleType": "time",
            # v1.1.0 built-in date formatting — no JavaScript needed
            "dateFormat":     "MMM d, YYYY",   # tooltip label
            "dateTickFormat": "M/d",            # axis tick labels
            "tickMinStep":    86400 * 1000 * 7, # min 1 week between ticks
            "tickNumber":     10,
            "tickLabelStyle": {"angle": 35, "fontSize": 11, "textAnchor": "start"},
            "height":         70,               # extra space for angled labels
        }],
        yAxis=[{"label": "Temperature (°F)", "min": 48, "max": 85}],
        grid={"horizontal": True},
        axisHighlight={"x": "line"},
        tooltip={"trigger": "axis"},
        margin={"left": 65, "right": 20, "top": 20, "bottom": 90},
        referenceLines=[
            {
                "y":          WARNING_THRESHOLD,
                "label":      "Warning (78°F)",
                "lineStyle":  {"stroke": "#ff9800", "strokeWidth": 2, "strokeDasharray": "5 5"},
                "labelStyle": {"fill": "#ff9800", "fontWeight": "bold"},
                "labelAlign": "end",
            },
            {
                "x":          event_ts,
                "label":      "Calibration",
                "lineStyle":  {"stroke": "#9c27b0", "strokeWidth": 2, "strokeDasharray": "4 4"},
                "labelStyle": {"fill": "#9c27b0"},
                "labelAlign": "start",
            },
        ],
    ),
    dmc.Alert(
        dmc.Code(
            'xAxis=[{"scaleType": "time", "data": epoch_ms, '
            '"dateFormat": "MMM d, YYYY", "dateTickFormat": "M/d", '
            '"tickMinStep": 86400_000 * 7}]'
        ),
        color="blue", variant="light",
    ),
], gap="sm"), p="lg", radius="md", style=GLASS)


# ---------------------------------------------------------------------------
# Chart 2 — axis highlight + click event
# ---------------------------------------------------------------------------
chart_click = dmc.Paper(dmc.Stack([
    dmc.Text("Axis Highlight & Click Events", fw=600),
    dmc.Text(
        "axisHighlight: {'x': 'line'} draws a crosshair; "
        "tooltip: {'trigger': 'axis'} shows all series. "
        "Click any point to capture data via the clickData output prop.",
        size="sm", c="dimmed",
    ),
    LineChart(
        id="mc-th-click",
        licenseKey=MUI_KEY,
        height=300,
        series=[
            {
                "data":  sensor_a,
                "label": "Sensor A",
                "color": "#1976d2",
                "curve": "monotoneX",
                "showMark": True,
                "highlightScope": {"highlight": "item", "fade": "global"},
            },
            {
                "data":  sensor_b,
                "label": "Sensor B",
                "color": "#7b1fa2",
                "curve": "monotoneX",
                "showMark": True,
                "highlightScope": {"highlight": "item", "fade": "global"},
            },
        ],
        xAxis=[{
            "data":      timestamps,
            "scaleType": "time",
            "dateFormat":     "MMM d",
            "dateTickFormat": "M/d",
            "tickMinStep":    86400 * 1000 * 14,
            "tickNumber":     6,
        }],
        yAxis=[{"label": "°F"}],
        grid={"horizontal": True},
        axisHighlight={"x": "line", "y": "line"},
        tooltip={"trigger": "item"},
        margin={"left": 55, "right": 20, "top": 20, "bottom": 50},
    ),
    dmc.Text("Click output:", size="xs", c="dimmed"),
    html.Pre(
        id="mc-th-click-out",
        children="Click a point to see data...",
        style={
            "fontSize": "11px", "margin": 0,
            "padding": "8px 12px", "borderRadius": "6px",
            "background": "light-dark(#f8f9fa, #1a1b1e)",
            "border": "1px solid light-dark(#dee2e6, #373a40)",
        },
    ),
], gap="sm"), p="lg", radius="md", style=GLASS)


component = dmc.Stack([
    dmc.Text("Tick Configuration & Date Formatting", fw=700, size="xl"),
    dmc.Text(
        "Best practices for time-scale axes — epoch-ms data, "
        "built-in date formatting (v1.1.0), angled ticks, reference lines, and click events.",
        size="sm", c="dimmed",
    ),
    chart_time_scale,
    chart_click,
], gap="lg")


# ---------------------------------------------------------------------------
# Callback — click event display
# ---------------------------------------------------------------------------
@callback(
    Output("mc-th-click-out", "children"),
    Input("mc-th-click",      "clickData"),
    prevent_initial_call=True,
)
def show_click(data):
    if not data:
        return "Click a point to see data..."
    return json.dumps(data, indent=2)
```


---

## Pie Charts



```python
# File: docs/dash_mui_charts/pie_charts.py

import random
import dash_mantine_components as dmc
from dash_mui_charts import PieChart

random.seed(77)

PALETTE = ['#1976d2', '#388e3c', '#f57c00', '#d32f2f', '#7b1fa2', '#00838f', '#c62828', '#4527a0']
GLASS = {
    "background": "light-dark(rgba(255,255,255,0.55), rgba(30,30,30,0.55))",
    "backdropFilter": "blur(16px) saturate(1.8)",
    "WebkitBackdropFilter": "blur(16px) saturate(1.8)",
    "border": "1px solid light-dark(rgba(255,255,255,0.5), rgba(255,255,255,0.08))",
}

market_data = [
    {'id': 0, 'value': 35, 'label': 'Chrome', 'color': '#4285f4'},
    {'id': 1, 'value': 25, 'label': 'Safari', 'color': '#007aff'},
    {'id': 2, 'value': 18, 'label': 'Firefox', 'color': '#ff7139'},
    {'id': 3, 'value': 12, 'label': 'Edge', 'color': '#0078d7'},
    {'id': 4, 'value': 10, 'label': 'Other', 'color': '#9e9e9e'},
]

sales_data = [
    {'id': i, 'value': random.randint(15, 50), 'label': cat}
    for i, cat in enumerate(['Electronics', 'Clothing', 'Food', 'Books', 'Toys'])
]

inner_data = [
    {'id': 0, 'value': 60, 'label': 'Desktop', 'color': '#1565c0'},
    {'id': 1, 'value': 35, 'label': 'Mobile', 'color': '#2e7d32'},
    {'id': 2, 'value': 5, 'label': 'Tablet', 'color': '#e65100'},
]
outer_data = [
    {'id': 0, 'value': 30, 'label': 'Windows', 'color': '#42a5f5'},
    {'id': 1, 'value': 20, 'label': 'macOS', 'color': '#66bb6a'},
    {'id': 2, 'value': 10, 'label': 'Linux', 'color': '#ef5350'},
    {'id': 3, 'value': 20, 'label': 'iOS', 'color': '#ab47bc'},
    {'id': 4, 'value': 15, 'label': 'Android', 'color': '#ffa726'},
    {'id': 5, 'value': 5, 'label': 'iPadOS', 'color': '#26c6da'},
]

component = dmc.Stack([
    dmc.Text("Pie Charts", fw=700, size="xl"),
    dmc.Text("Pie, donut, nested, and gauge-arc variations.", size="sm", c="dimmed"),

    dmc.SimpleGrid(cols={"base": 1, "md": 2}, children=[
        dmc.Paper(
            dmc.Stack([
                dmc.Text("Pie Chart", fw=600),
                dmc.Text("Browser market share with arc labels.", size="sm", c="dimmed"),
                PieChart(id="mc-pie-basic", data=market_data, arcLabel='formattedValue', arcLabelMinAngle=25, colors=PALETTE, height=280),
            ], gap="sm"),
            p="lg", radius="md", style=GLASS,
        ),
        dmc.Paper(
            dmc.Stack([
                dmc.Text("Donut Chart", fw=600),
                dmc.Text("Sales by category with inner radius cutout.", size="sm", c="dimmed"),
                PieChart(id="mc-pie-donut", data=sales_data, innerRadius=60, cornerRadius=4, paddingAngle=2, colors=PALETTE, height=280),
            ], gap="sm"),
            p="lg", radius="md", style=GLASS,
        ),
    ]),

    dmc.Paper(
        dmc.Stack([
            dmc.Text("Nested Concentric Pie", fw=600),
            dmc.Text("Inner ring: device type. Outer ring: OS breakdown.", size="sm", c="dimmed"),
            PieChart(
                id="mc-pie-nested",
                series=[
                    {'data': inner_data, 'innerRadius': 0, 'outerRadius': 70, 'highlightScope': {'fade': 'global', 'highlight': 'item'}},
                    {'data': outer_data, 'innerRadius': 80, 'outerRadius': 110, 'highlightScope': {'fade': 'global', 'highlight': 'item'}},
                ],
                height=300,
            ),
        ], gap="sm"),
        p="lg", radius="md", style=GLASS,
    ),

    dmc.Paper(
        dmc.Stack([
            dmc.Text("Gauge-Style Arc", fw=600),
            dmc.Text("Half-circle gauge using startAngle/endAngle.", size="sm", c="dimmed"),
            dmc.SimpleGrid(cols=2, children=[
                PieChart(
                    id=f"mc-pie-gauge-{i}",
                    data=[
                        {'id': 0, 'value': val, 'label': label, 'color': color},
                        {'id': 1, 'value': 100 - val, 'color': 'light-dark(#e0e0e0, #424242)'},
                    ],
                    startAngle=-90, endAngle=90, innerRadius=50, outerRadius=80, arcLabel='value', height=160,
                )
                for i, (val, label, color) in enumerate([
                    (72, 'CPU', '#1976d2'), (88, 'Disk', '#f57c00'),
                ])
            ]),
        ], gap="sm"),
        p="lg", radius="md", style=GLASS,
    ),
], gap="lg")
```


---

## Scatter Charts



```python
# File: docs/dash_mui_charts/scatter_charts.py

import random
import dash_mantine_components as dmc
from dash_mui_charts import ScatterChart

random.seed(55)

GLASS = {
    "background": "light-dark(rgba(255,255,255,0.55), rgba(30,30,30,0.55))",
    "backdropFilter": "blur(16px) saturate(1.8)",
    "WebkitBackdropFilter": "blur(16px) saturate(1.8)",
    "border": "1px solid light-dark(rgba(255,255,255,0.5), rgba(255,255,255,0.08))",
}


def make_cluster(cx, cy, n=30, spread=15):
    return [{'x': round(cx + random.gauss(0, spread), 1), 'y': round(cy + random.gauss(0, spread), 1), 'id': i} for i in range(n)]

cluster_a = make_cluster(150, 200, 40)
cluster_b = make_cluster(300, 150, 35)
cluster_c = make_cluster(220, 350, 30)

z_data = [{'x': round(random.uniform(0, 100), 1), 'y': round(random.uniform(0, 100), 1), 'z': round(random.uniform(0, 100), 1), 'id': i} for i in range(60)]

component = dmc.Stack([
    dmc.Text("Scatter Charts", fw=700, size="xl"),
    dmc.Text("Multi-series scatter plots with clustering, z-axis coloring, and voronoi interaction.", size="sm", c="dimmed"),

    dmc.Paper(
        dmc.Stack([
            dmc.Text("Multi-Series Clusters", fw=600),
            dmc.Text("Three distinct clusters with voronoi hover detection.", size="sm", c="dimmed"),
            ScatterChart(
                id="mc-scatter-clusters",
                series=[
                    {'id': 'cluster-a', 'label': 'Group A', 'data': cluster_a, 'color': '#1976d2', 'markerSize': 5},
                    {'id': 'cluster-b', 'label': 'Group B', 'data': cluster_b, 'color': '#388e3c', 'markerSize': 5},
                    {'id': 'cluster-c', 'label': 'Group C', 'data': cluster_c, 'color': '#f57c00', 'markerSize': 5},
                ],
                xAxis=[{'label': 'Feature X', 'min': 50, 'max': 400}],
                yAxis=[{'label': 'Feature Y', 'min': 50, 'max': 450}],
                voronoiMaxRadius=40,
                grid={'horizontal': True, 'vertical': True},
                height=380,
            ),
        ], gap="sm"),
        p="lg", radius="md", style=GLASS,
    ),

    dmc.Paper(
        dmc.Stack([
            dmc.Text("Z-Axis Color Mapping", fw=600),
            dmc.Text("Third dimension mapped to a continuous color scale.", size="sm", c="dimmed"),
            ScatterChart(
                id="mc-scatter-zaxis",
                series=[
                    {'id': 'z-series', 'label': 'Intensity', 'data': z_data, 'markerSize': 7, 'highlightScope': {'highlight': 'item', 'fade': 'global'}},
                ],
                zAxis=[{'colorMap': {'type': 'continuous', 'min': 0, 'max': 100, 'color': ['#e3f2fd', '#1565c0']}}],
                xAxis=[{'label': 'X', 'min': -5, 'max': 105}],
                yAxis=[{'label': 'Y', 'min': -5, 'max': 105}],
                voronoiMaxRadius=25,
                grid={'horizontal': True, 'vertical': True},
                height=380,
            ),
        ], gap="sm"),
        p="lg", radius="md", style=GLASS,
    ),
], gap="lg")
```


---

## Composite Charts



```python
# File: docs/dash_mui_charts/composite_charts.py

import os
import random
import math
import dash_mantine_components as dmc
from dash_mui_charts import CompositeChart

MUI_KEY = os.getenv('MUI_PRO_API_KEY', '')
random.seed(33)

GLASS = {
    "background": "light-dark(rgba(255,255,255,0.55), rgba(30,30,30,0.55))",
    "backdropFilter": "blur(16px) saturate(1.8)",
    "WebkitBackdropFilter": "blur(16px) saturate(1.8)",
    "border": "1px solid light-dark(rgba(255,255,255,0.5), rgba(255,255,255,0.08))",
}


n = 50
scatter_data = [{'x': i, 'y': round(20 + 0.4 * i + random.gauss(0, 4), 1), 'id': i} for i in range(n)]
trend_line = [round(20 + 0.4 * i, 1) for i in range(n)]

component = dmc.Stack([
    dmc.Text("Composite Charts", fw=700, size="xl"),
    dmc.Text("Layer scatter points and line series on a single chart surface.", size="sm", c="dimmed"),

    dmc.Paper(
        dmc.Stack([
            dmc.Text("Scatter + Trend Line", fw=600),
            dmc.Text("Sensor readings with a linear regression overlay and reference limits.", size="sm", c="dimmed"),
            CompositeChart(
                id="mc-composite-trend", licenseKey=MUI_KEY,
                series=[
                    {'type': 'scatter', 'id': 'readings', 'label': 'Readings', 'data': scatter_data, 'markerSize': 4, 'color': '#1976d2'},
                    {'type': 'line', 'id': 'trend', 'label': 'Trend', 'data': trend_line, 'curve': 'linear', 'showMark': False, 'color': '#ff7043'},
                ],
                xAxis=[{'id': 'x', 'data': list(range(n)), 'scaleType': 'linear', 'label': 'Sample', 'zoom': {'minSpan': 10, 'panning': True}}],
                yAxis=[{'label': 'Value'}],
                referenceLines=[
                    {'y': 35, 'label': 'Upper limit', 'lineStyle': {'stroke': '#d32f2f', 'strokeDasharray': '6 3'}},
                    {'y': 15, 'label': 'Lower limit', 'lineStyle': {'stroke': '#1565c0', 'strokeDasharray': '6 3'}},
                ],
                initialZoom=[{'axisId': 'x', 'start': 0, 'end': 60}],
                showSlider=True, voronoiMaxRadius=20,
                grid={'horizontal': True},
                height=400,
            ),
        ], gap="sm"),
        p="lg", radius="md", style=GLASS,
    ),

    dmc.Paper(
        dmc.Stack([
            dmc.Text("Biaxial Composite", fw=600),
            dmc.Text("Temperature scatter on left axis, humidity line on right axis.", size="sm", c="dimmed"),
            CompositeChart(
                id="mc-composite-biaxial", licenseKey=MUI_KEY,
                series=[
                    {'type': 'scatter', 'id': 'temp', 'label': 'Temperature (°C)',
                     'data': [{'x': i, 'y': round(22 + random.gauss(0, 3), 1), 'id': i} for i in range(24)],
                     'markerSize': 5, 'color': '#ef5350', 'yAxisId': 'left'},
                    {'type': 'line', 'id': 'humidity', 'label': 'Humidity (%)',
                     'data': [round(60 + 15 * math.sin(2 * math.pi * i / 24) + random.uniform(-3, 3), 1) for i in range(24)],
                     'curve': 'natural', 'showMark': False, 'color': '#42a5f5', 'area': True, 'yAxisId': 'right'},
                ],
                xAxis=[{'data': list(range(24)), 'scaleType': 'linear', 'label': 'Hour'}],
                yAxis=[{'id': 'left', 'label': '°C', 'position': 'left'}, {'id': 'right', 'label': '%', 'position': 'right'}],
                grid={'horizontal': True},
                height=350,
            ),
        ], gap="sm"),
        p="lg", radius="md", style=GLASS,
    ),
], gap="lg")
```


---

## Heatmap Charts



```python
# File: docs/dash_mui_charts/heatmap_charts.py

import os
import random
import dash_mantine_components as dmc
from dash_mui_charts import Heatmap

MUI_KEY = os.getenv('MUI_PRO_API_KEY', '')
random.seed(88)

GLASS = {
    "background": "light-dark(rgba(255,255,255,0.55), rgba(30,30,30,0.55))",
    "backdropFilter": "blur(16px) saturate(1.8)",
    "WebkitBackdropFilter": "blur(16px) saturate(1.8)",
    "border": "1px solid light-dark(rgba(255,255,255,0.5), rgba(255,255,255,0.08))",
}


days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
hours = [f'{h}:00' for h in range(6, 22)]

activity_data = []
for xi, day in enumerate(days):
    for yi, hour in enumerate(hours):
        base = 30 if xi < 5 and 3 <= yi <= 10 else 10
        val = max(0, min(100, base + random.randint(-10, 40)))
        activity_data.append([xi, yi, val])

labels = ['Revenue', 'Traffic', 'Conversions', 'Ad Spend', 'Satisfaction']
corr_data = []
for i in range(len(labels)):
    for j in range(len(labels)):
        val = 1.0 if i == j else round(random.uniform(-0.3, 0.95), 2)
        corr_data.append([i, j, val])

component = dmc.Stack([
    dmc.Text("Heatmap Charts", fw=700, size="xl"),
    dmc.Group([
        dmc.Text("Matrix visualization with continuous and piecewise color scales.", size="sm", c="dimmed"),
        dmc.Badge("Pro Feature", color="violet", variant="light", size="sm"),
    ]),

    dmc.Paper(
        dmc.Stack([
            dmc.Text("Website Activity Heatmap", fw=600),
            dmc.Text("Visitor activity by day and hour. Darker = more active.", size="sm", c="dimmed"),
            Heatmap(
                id="mc-heatmap-activity", licenseKey=MUI_KEY,
                data=activity_data,
                xAxis={'data': days, 'label': 'Day'},
                yAxis={'data': hours, 'label': 'Hour'},
                colorScale={'type': 'continuous', 'min': 0, 'max': 100, 'colors': ['#e3f2fd', '#1565c0']},
                height=400,
            ),
        ], gap="sm"),
        p="lg", radius="md", style=GLASS,
    ),

    dmc.Paper(
        dmc.Stack([
            dmc.Text("Correlation Matrix", fw=600),
            dmc.Text("Business metric correlations with diverging color scale.", size="sm", c="dimmed"),
            Heatmap(
                id="mc-heatmap-corr", licenseKey=MUI_KEY,
                data=corr_data,
                xAxis={'data': labels},
                yAxis={'data': labels},
                colorScale={'type': 'piecewise', 'thresholds': [-0.5, 0, 0.5], 'colors': ['#d32f2f', '#ffcdd2', '#c8e6c9', '#2e7d32']},
                height=350,
            ),
        ], gap="sm"),
        p="lg", radius="md", style=GLASS,
    ),
], gap="lg")
```


---

## Sparkline Charts



```python
# File: docs/dash_mui_charts/sparkline_charts.py

import random
import dash_mantine_components as dmc
from dash_mui_charts import SparklineChart

random.seed(66)

GLASS = {
    "background": "light-dark(rgba(255,255,255,0.55), rgba(30,30,30,0.55))",
    "backdropFilter": "blur(16px) saturate(1.8)",
    "WebkitBackdropFilter": "blur(16px) saturate(1.8)",
    "border": "1px solid light-dark(rgba(255,255,255,0.5), rgba(255,255,255,0.08))",
}

def spark_data(n=12, base=50, spread=20):
    return [max(0, round(base + random.uniform(-spread, spread), 1)) for _ in range(n)]

revenue_spark = spark_data(12, 60, 15)
users_spark = spark_data(12, 200, 50)
errors_spark = [random.randint(0, 8) for _ in range(12)]
cpu_spark = spark_data(20, 45, 25)

component = dmc.Stack([
    dmc.Text("Sparkline Charts", fw=700, size="xl"),
    dmc.Text("Compact inline charts for dashboards, tables, and KPI cards.", size="sm", c="dimmed"),

    dmc.SimpleGrid(cols={"base": 1, "sm": 2, "md": 4}, children=[
        dmc.Paper(
            dmc.Stack([
                dmc.Text("Revenue", size="sm", c="dimmed"),
                dmc.Group([dmc.Text(f"${sum(revenue_spark):.0f}k", fw=700, size="xl"), dmc.Badge("+12%", color="green", variant="light", size="sm")], justify="space-between"),
                SparklineChart(id="mc-spark-revenue", data=revenue_spark, plotType='line', color='#1976d2', area=True, curve='monotoneX', height=40, showTooltip=True),
            ], gap="xs"),
            p="md", radius="md", style=GLASS,
        ),
        dmc.Paper(
            dmc.Stack([
                dmc.Text("Active Users", size="sm", c="dimmed"),
                dmc.Group([dmc.Text(f"{users_spark[-1]:.0f}", fw=700, size="xl"), dmc.Badge("+8%", color="green", variant="light", size="sm")], justify="space-between"),
                SparklineChart(id="mc-spark-users", data=users_spark, plotType='line', color='#388e3c', area=True, curve='monotoneX', height=40, showTooltip=True),
            ], gap="xs"),
            p="md", radius="md", style=GLASS,
        ),
        dmc.Paper(
            dmc.Stack([
                dmc.Text("Errors", size="sm", c="dimmed"),
                dmc.Group([dmc.Text(f"{errors_spark[-1]}", fw=700, size="xl"), dmc.Badge("-3%", color="red", variant="light", size="sm")], justify="space-between"),
                SparklineChart(id="mc-spark-errors", data=errors_spark, plotType='bar', color='#ef5350', height=40, showTooltip=True),
            ], gap="xs"),
            p="md", radius="md", style=GLASS,
        ),
        dmc.Paper(
            dmc.Stack([
                dmc.Text("CPU Load", size="sm", c="dimmed"),
                dmc.Group([dmc.Text(f"{cpu_spark[-1]:.0f}%", fw=700, size="xl"), dmc.Badge("stable", color="gray", variant="light", size="sm")], justify="space-between"),
                SparklineChart(id="mc-spark-cpu", data=cpu_spark, plotType='line', color='#f57c00', curve='linear', height=40, showTooltip=True),
            ], gap="xs"),
            p="md", radius="md", style=GLASS,
        ),
    ]),

    dmc.Paper(
        dmc.Stack([
            dmc.Text("Sparkline Variations", fw=600),
            dmc.Text("Different plot types, curves, and configurations.", size="sm", c="dimmed"),
            dmc.SimpleGrid(cols=3, children=[
                dmc.Stack([
                    dmc.Text("Line (monotoneX)", size="xs", c="dimmed", ta="center"),
                    SparklineChart(id="mc-spark-v1", data=spark_data(), plotType='line', color='#1976d2', curve='monotoneX', height=60, area=True, showTooltip=True),
                ], gap="xs"),
                dmc.Stack([
                    dmc.Text("Line (step)", size="xs", c="dimmed", ta="center"),
                    SparklineChart(id="mc-spark-v2", data=spark_data(), plotType='line', color='#7b1fa2', curve='step', height=60, showTooltip=True),
                ], gap="xs"),
                dmc.Stack([
                    dmc.Text("Bar", size="xs", c="dimmed", ta="center"),
                    SparklineChart(id="mc-spark-v3", data=spark_data(8, 30, 20), plotType='bar', color='#388e3c', height=60, showTooltip=True),
                ], gap="xs"),
            ]),
        ], gap="sm"),
        p="lg", radius="md", style=GLASS,
    ),
], gap="lg")
```


---

## Live Trading Chart

Real-time OHLCV candlestick streaming with volume bars, a forecast line with uncertainty bands, and automatic swing-point alert labels. All simulation parameters are controllable at runtime.

| Feature | Description |
|:--------|:------------|
| **Candlesticks** | OHLCV bars with green/red coloring |
| **Volume** | Optional volume histogram (bottom panel) |
| **Forecast** | Forward projection with uncertainty shading |
| **Alerts** | Configurable via `alertProbability` (chance per tick) and `alertThresholdPct` (minimum % move) |
| **Alert colors** | `alertUpColor` / `alertDownColor` for label styling |
| **Zoom / Slider** | Pro — range slider preview |



```python
# File: docs/dash_mui_charts/live_trading.py

"""Live Trading Chart — real-time OHLCV candlestick simulation with swing-point alerts."""
import os
import dash_mantine_components as dmc
from dash import html, callback, Input, Output, State, ctx
from dash_mui_charts import LiveTradingChart

MUI_KEY = os.getenv('MUI_PRO_API_KEY', '')

GLASS = {
    "background": "light-dark(rgba(255,255,255,0.55), rgba(30,30,30,0.55))",
    "backdropFilter": "blur(16px) saturate(1.8)",
    "WebkitBackdropFilter": "blur(16px) saturate(1.8)",
    "border": "1px solid light-dark(rgba(255,255,255,0.5), rgba(255,255,255,0.08))",
}

component = dmc.Stack([
    dmc.Text("Live Trading Chart", fw=700, size="xl"),
    dmc.Text(
        "Real-time candlestick simulation with OHLCV data, volume bars, "
        "forecast line with uncertainty bands, and swing-point alert labels. "
        "Alerts only fire at confirmed local highs and lows — not on every candle.",
        size="sm", c="dimmed",
    ),

    dmc.Paper(dmc.Stack([

        # ── Controls Row 1: Buttons + Toggles ─────────────────────────────
        dmc.Group([
            dmc.Group([
                dmc.Button("Start", id="mc-lt-start", color="green", size="sm"),
                dmc.Button("Stop",  id="mc-lt-stop",  color="yellow", size="sm", variant="outline"),
                dmc.Button("Reset", id="mc-lt-reset", color="red",    size="sm", variant="outline"),
            ], gap="xs"),
            dmc.Group([
                dmc.Switch(id="mc-lt-volume",  label="Volume",       checked=True,  size="sm"),
                dmc.Switch(id="mc-lt-labels",  label="Price Labels", checked=False, size="sm"),
                dmc.Switch(id="mc-lt-slider",  label="Zoom Preview", checked=True,  size="sm"),
            ], gap="md"),
        ], justify="space-between"),

        # ── Controls Row 2: Sliders ────────────────────────────────────────
        dmc.SimpleGrid(cols={"base": 1, "sm": 2, "lg": 4}, children=[
            dmc.Stack([
                dmc.Text("Speed (ms/tick)", size="xs", fw=500),
                dmc.Slider(
                    id="mc-lt-speed", value=200, min=50, max=1000, step=50,
                    marks=[
                        {"value": 50,   "label": "50"},
                        {"value": 200,  "label": "200"},
                        {"value": 500,  "label": "500"},
                        {"value": 1000, "label": "1s"},
                    ],
                ),
            ], gap=4),
            dmc.Stack([
                dmc.Text("Volatility", size="xs", fw=500),
                dmc.Slider(
                    id="mc-lt-volatility", value=2.0, min=0.5, max=8.0, step=0.5,
                    marks=[
                        {"value": 0.5, "label": "0.5"},
                        {"value": 2.0, "label": "2.0"},
                        {"value": 8.0, "label": "8.0"},
                    ],
                ),
            ], gap=4),
            dmc.Stack([
                dmc.Text("Drift", size="xs", fw=500),
                dmc.Slider(
                    id="mc-lt-drift", value=0.1, min=-0.5, max=0.5, step=0.05,
                    marks=[
                        {"value": -0.5, "label": "-0.5"},
                        {"value": 0,    "label": "0"},
                        {"value": 0.5,  "label": "+0.5"},
                    ],
                ),
            ], gap=4),
            dmc.Stack([
                dmc.Text("Window", size="xs", fw=500),
                dmc.Slider(
                    id="mc-lt-window", value=80, min=30, max=200, step=10,
                    marks=[
                        {"value": 30,  "label": "30"},
                        {"value": 80,  "label": "80"},
                        {"value": 200, "label": "200"},
                    ],
                ),
            ], gap=4),
        ]),

        # ── Stats Row ──────────────────────────────────────────────────────
        dmc.Group([
            dmc.Stack([
                dmc.Text("Price",  size="xs", c="dimmed"),
                dmc.Text(id="mc-lt-price",  children="$100.00", fw=700, size="lg"),
            ], gap=0, align="center"),
            dmc.Stack([
                dmc.Text("Ticks",  size="xs", c="dimmed"),
                dmc.Text(id="mc-lt-ticks",  children="0",       fw=700, size="lg"),
            ], gap=0, align="center"),
            dmc.Stack([
                dmc.Text("Alerts", size="xs", c="dimmed"),
                dmc.Text(id="mc-lt-alerts", children="0",       fw=700, size="lg"),
            ], gap=0, align="center"),
            dmc.Stack([
                dmc.Text("Status", size="xs", c="dimmed"),
                dmc.Text(id="mc-lt-status", children="Stopped", fw=700, size="lg", c="dimmed"),
            ], gap=0, align="center"),
        ], gap="xl"),

        # ── Chart ──────────────────────────────────────────────────────────
        LiveTradingChart(
            id="mc-lt-chart",
            licenseKey=MUI_KEY,
            height=520,
            running=False,
            intervalMs=200,
            seed=42,
            windowSize=80,
            forecastSize=20,
            initialPrice=100,
            volatility=0.02,
            drift=0.001,
            forecastVolatility=1.5,
            # Alert tuning — low probability + high threshold keeps labels sparse
            alertProbability=0.03,   # 3% chance per tick (vs default 8%)
            alertThresholdPct=3.0,   # minimum 3% move to qualify
            alertUpColor="#4caf50",
            alertDownColor="#f44336",
            showVolume=True,
            showLabels=False,
            showSlider=True,
            volumeHeightPct=20,
            margin={"left": 75, "right": 30, "top": 20, "bottom": 50},
        ),

        # ── Alert History ──────────────────────────────────────────────────
        dmc.Text("Recent Alerts", fw=600, size="sm"),
        html.Pre(
            id="mc-lt-log",
            children="Alerts will appear here once running...",
            style={
                "fontSize": "11px", "maxHeight": "130px",
                "overflow": "auto", "margin": 0,
                "padding": "8px 12px", "borderRadius": "6px",
                "background": "light-dark(#f8f9fa, #1a1b1e)",
                "border": "1px solid light-dark(#dee2e6, #373a40)",
            },
        ),

    ], gap="md"), p="lg", radius="md", style=GLASS),
], gap="md")


# ── Callbacks ─────────────────────────────────────────────────────────────────

@callback(
    Output("mc-lt-chart", "running"),
    Input("mc-lt-start", "n_clicks"),
    Input("mc-lt-stop",  "n_clicks"),
    prevent_initial_call=True,
)
def toggle_running(_start, _stop):
    return ctx.triggered_id == "mc-lt-start"


@callback(
    Output("mc-lt-chart", "resetTrigger"),
    Input("mc-lt-reset", "n_clicks"),
    prevent_initial_call=True,
)
def reset_chart(n):
    return n or 0


@callback(Output("mc-lt-chart", "intervalMs"),  Input("mc-lt-speed",      "value"))
def set_speed(val):      return val or 200

@callback(Output("mc-lt-chart", "volatility"),  Input("mc-lt-volatility", "value"))
def set_volatility(val): return (val or 2.0) / 100

@callback(Output("mc-lt-chart", "drift"),       Input("mc-lt-drift",      "value"))
def set_drift(val):      return (val or 0) / 100

@callback(Output("mc-lt-chart", "windowSize"),  Input("mc-lt-window",     "value"))
def set_window(val):     return val or 80

@callback(Output("mc-lt-chart", "showVolume"),  Input("mc-lt-volume",     "checked"))
def set_volume(v):       return v if v is not None else True

@callback(Output("mc-lt-chart", "showLabels"),  Input("mc-lt-labels",     "checked"))
def set_labels(v):       return v if v is not None else False

@callback(Output("mc-lt-chart", "showSlider"),  Input("mc-lt-slider",     "checked"))
def set_slider(v):       return v if v is not None else True


@callback(
    Output("mc-lt-price",  "children"),
    Output("mc-lt-price",  "c"),
    Output("mc-lt-ticks",  "children"),
    Output("mc-lt-status", "children"),
    Output("mc-lt-status", "c"),
    Input("mc-lt-chart",   "currentPrice"),
    Input("mc-lt-chart",   "tickCount"),
    State("mc-lt-chart",   "running"),
)
def update_stats(price, ticks, running):
    p = price if price is not None else 100.0
    return (
        f"${p:,.2f}",
        "green" if p >= 100 else "red",
        str(ticks or 0),
        "Running" if running else "Stopped",
        "green" if running else "dimmed",
    )


@callback(
    Output("mc-lt-alerts", "children"),
    Output("mc-lt-log",    "children"),
    Input("mc-lt-chart",   "alertHistory"),
)
def update_alerts(alerts):
    if not alerts:
        return "0", "Alerts will appear here once running..."
    recent = list(reversed(alerts[-15:]))
    lines = [
        f"[Tick {a.get('tick', '?'):>5}]  "
        f"{'UP' if a.get('type') == 'up' else 'DN'}  "
        f"${a.get('price', 0):>8.2f}  ({a.get('message', '')})"
        for a in recent
    ]
    return str(len(alerts)), "\n".join(lines)
```


---

## LineChart Properties

| Property | Type | Default | Description |
|:---------|:-----|:--------|:------------|
| `series` | list | Required | Array of series objects with `data`, `label`, `color`, `curve`, `area`, `stack` |
| `xAxis` | list | `None` | X-axis config: `data`, `scaleType`, `label`, `zoom` (Pro) |
| `yAxis` | list | `None` | Y-axis config: `label`, `position`, `min`, `max` |
| `referenceLines` | list | `None` | Horizontal/vertical reference lines |
| `licenseKey` | string | `""` | MUI Pro license key (for zoom, brush, slider) |
| `initialZoom` | list | `None` | Initial zoom state (Pro) |
| `showSlider` | bool | `False` | Show zoom range slider (Pro) |
| `showToolbar` | bool | `False` | Show zoom/export toolbar (Pro) |
| `brushConfig` | dict | `None` | Brush selection config (Pro) |
| `grid` | dict | `None` | Grid lines: `{horizontal: bool, vertical: bool}` |
| `height` | int | `300` | Chart height in pixels |
| `colors` | list | `None` | Custom color palette |
| `axisHighlight` | dict | `None` | `{x: 'none'/'line'/'band', y: 'none'/'line'}` |
| `tooltip` | dict | `None` | `{trigger: 'axis'/'item'/'none'}` |
| `clickData` | dict | Output | Click event data |
| `highlightedItem` | dict | `None` | Controlled highlight state |
| `dateFormat` | string | `None` | **1.1.0** — Tooltip date format for `scaleType: 'time'` (e.g. `"MMM d, YYYY"`) |
| `dateTickFormat` | string | `None` | **1.1.0** — Axis tick date format for `scaleType: 'time'` (e.g. `"M/d"`) |

---

## PieChart Properties

| Property | Type | Default | Description |
|:---------|:-----|:--------|:------------|
| `data` | list | `None` | Array of `{id, value, label, color}` objects |
| `series` | list | `None` | Multi-series for nested pies |
| `innerRadius` | int | `0` | Donut hole radius (>0 = donut) |
| `outerRadius` | int | `None` | Outer radius |
| `cornerRadius` | int | `0` | Rounded slice corners |
| `paddingAngle` | int | `0` | Gap between slices (degrees) |
| `startAngle` | int | `0` | Arc start angle |
| `endAngle` | int | `360` | Arc end angle |
| `arcLabel` | string | `None` | `'value'`, `'label'`, `'formattedValue'` |
| `arcLabelMinAngle` | int | `0` | Min angle to show label |
| `height` | int | `300` | Chart height |

---

## ScatterChart Properties

| Property | Type | Default | Description |
|:---------|:-----|:--------|:------------|
| `series` | list | Required | Array of series with `data: [{x, y, z?, id}]` |
| `zAxis` | list | `None` | Z-axis color mapping config |
| `voronoiMaxRadius` | int | `None` | Proximity hover radius |
| `xAxis` | list | `None` | X-axis config |
| `yAxis` | list | `None` | Y-axis config |
| `height` | int | `300` | Chart height |

---

## Heatmap Properties (Pro)

| Property | Type | Default | Description |
|:---------|:-----|:--------|:------------|
| `data` | list | Required | Array of `[x_index, y_index, value]` |
| `xAxis` | dict | Required | `{data: [...], label: "..."}` |
| `yAxis` | dict | Required | `{data: [...], label: "..."}` |
| `colorScale` | dict | Required | `{type, min, max, colors}` or `{type, thresholds, colors}` |
| `licenseKey` | string | Required | MUI Pro license key |
| `height` | int | `300` | Chart height |

---

## LiveTradingChart Properties

| Property | Type | Default | Description |
|:---------|:-----|:--------|:------------|
| `running` | bool | `False` | Start / stop the simulation tick |
| `intervalMs` | int | `200` | Milliseconds between ticks |
| `windowSize` | int | `80` | Number of visible candles |
| `forecastSize` | int | `20` | Candles projected ahead |
| `initialPrice` | float | `100` | Starting price |
| `volatility` | float | `0.02` | Per-tick price volatility (e.g. `0.02` = 2%) |
| `drift` | float | `0.001` | Per-tick price drift (positive = upward trend) |
| `forecastVolatility` | float | `1.5` | Uncertainty band width multiplier |
| `seed` | int | `None` | Random seed for reproducibility |
| `showVolume` | bool | `True` | Show volume histogram |
| `showLabels` | bool | `False` | Show price labels on candles |
| `showSlider` | bool | `False` | Show zoom range slider (Pro) |
| `volumeHeightPct` | int | `20` | Volume panel height as % of chart height |
| `alertProbability` | float | `0.08` | Probability of an alert firing per tick (lower = fewer labels) |
| `alertThresholdPct` | float | `2.0` | Minimum % price change required to trigger an alert |
| `alertUpColor` | string | `"#4caf50"` | Label color for upward alert moves |
| `alertDownColor` | string | `"#f44336"` | Label color for downward alert moves |
| `margin` | dict | `None` | Chart margins `{left, right, top, bottom}` |
| `licenseKey` | string | `""` | MUI Pro license key |
| `resetTrigger` | int | `None` | Increment to reset simulation |
| `currentPrice` | float | Output | Current simulated price |
| `tickCount` | int | Output | Number of ticks elapsed |
| `alertHistory` | list | Output | Array of `{type, tick, price, message}` alert objects |

---

## Contributing

Contributions welcome! Visit the [GitHub repo](https://github.com/pip-install-python/dash-mui-charts/issues).

## License

MIT License (component). MUI X Pro license required for Pro features.
