Display Charts

Charts

Zero-dependency SVG chart suite. Reads CSS custom properties at render time so dark-mode and theme switching are automatic. Declare data inline with a <script type="application/json"> child, fetch it remotely via data-wf-src, or push updates programmatically via el._wfChart.update(data).

Area

Use data-wf-chart="area" for a single-dataset filled area chart. The fill uses a gradient anchored to the series color and adapts automatically in dark mode. Add data-wf-legend to render a series legend below the chart.

HTML
<div data-wf-chart="area"
     data-wf-height="280"
     data-wf-label="Monthly Revenue"
     data-wf-legend
     data-wf-format="currency">
  <script type="application/json">
  {
    "labels": ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],
    "datasets": [{ "label": "Revenue", "data": [18400, 21200, 19800, 24600, 26100, 28400, 31200, 29800, 33400, 35100, 38600, 42000] }]
  }
  </script>
</div>

Multi-series

Use data-wf-chart="area-multi" to overlay multiple datasets. Each series is rendered as a separate line with its own fill gradient.

HTML
<div data-wf-chart="area-multi"
     data-wf-height="280"
     data-wf-label="Revenue vs Expenses"
     data-wf-legend
     data-wf-format="currency">
  <script type="application/json">
  {
    "labels": ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],
    "datasets": [
      { "label": "Revenue",  "data": [18400, 21200, 19800, 24600, 26100, 28400, 31200, 29800, 33400, 35100, 38600, 42000] },
      { "label": "Expenses", "data": [14200, 15800, 16100, 17400, 18200, 19600, 20800, 21200, 22400, 23100, 24600, 26200] }
    ]
  }
  </script>
</div>

Line

Use data-wf-chart="line" for a line chart without the area fill. Add data-wf-curve="straight" to use straight segments instead of the default smooth Catmull-Rom interpolation.

Smooth

HTML
<div data-wf-chart="line"
     data-wf-height="240"
     data-wf-label="Active Users">
  <script type="application/json">
  {
    "labels": ["Jan","Feb","Mar", …],
    "datasets": [{ "label": "Users", "data": [4200, 4800, …] }]
  }
  </script>
</div>

Straight + no grid

HTML
<div data-wf-chart="line"
     data-wf-height="240"
     data-wf-curve="straight"
     data-wf-grid="false">
  <!-- same JSON -->
</div>

<!-- curve: "smooth" (default) | "straight" -->
<!-- data-wf-grid="false" hides grid lines   -->

Multi-series

Use data-wf-chart="line-multi" to draw multiple lines on the same chart. The crosshair and tooltip track all series simultaneously.

HTML
<div data-wf-chart="line-multi"
     data-wf-height="280"
     data-wf-label="Traffic by Channel"
     data-wf-legend>
  <script type="application/json">
  {
    "labels": ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],
    "datasets": [
      { "label": "Organic", "data": [3200, 3600, 4100, 4800, 5200, 5800, 6400, 6100, 6800, 7200, 7800, 8400] },
      { "label": "Paid",    "data": [1800, 2100, 2400, 2800, 3100, 3400, 3800, 3600, 4000, 4300, 4700, 5100] },
      { "label": "Referral","data": [800,  900,  1100, 1200, 1400, 1600, 1700, 1800, 2000, 2100, 2300, 2600] }
    ]
  }
  </script>
</div>

Bar

Use data-wf-chart="bar" for a single-series vertical bar chart. Bars animate up from the baseline on first render.

HTML
<div data-wf-chart="bar"
     data-wf-height="280"
     data-wf-label="Monthly Orders">
  <script type="application/json">
  {
    "labels": ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],
    "datasets": [{ "label": "Orders", "data": [340, 410, 380, 490, 520, 560, 620, 590, 670, 710, 780, 850] }]
  }
  </script>
</div>

Grouped (multi-series)

Use data-wf-chart="bar-multi" to render grouped bars — one bar per series per category, side by side.

HTML
<div data-wf-chart="bar-multi"
     data-wf-height="280"
     data-wf-label="Quarterly Revenue"
     data-wf-legend
     data-wf-format="currency">
  <script type="application/json">
  {
    "labels": ["Q1","Q2","Q3","Q4"],
    "datasets": [
      { "label": "2024", "data": [59400, 79100, 94400, 125800] },
      { "label": "2023", "data": [48200, 62400, 78100, 101200] }
    ]
  }
  </script>
</div>

Horizontal Bar

Use data-wf-chart="bar-horizontal" for a ranked list layout. Category labels are shown on the left; bars grow from left to right. Only the first dataset is rendered.

HTML
<div data-wf-chart="bar-horizontal"
     data-wf-height="240"
     data-wf-label="Top Products by Revenue"
     data-wf-format="currency">
  <script type="application/json">
  {
    "labels": ["Analytics Pro","CRM Suite","Email Tools","File Storage","Video Conf"],
    "datasets": [{ "label": "Revenue", "data": [48200, 36700, 29400, 24100, 18600] }]
  }
  </script>
</div>

Stacked

Add data-wf-stacked to an area-multi chart to stack series on top of each other. The Y-axis maximum reflects the column-wise sum. Useful for visualising composition over time.

HTML
<div data-wf-chart="area-multi"
     data-wf-height="280"
     data-wf-label="Traffic by Channel"
     data-wf-legend
     data-wf-stacked>
  <!-- same JSON as area-multi -->
</div>

<!-- data-wf-stacked requires data-wf-chart="area-multi" -->

Doughnut

Use data-wf-chart="doughnut" for a doughnut chart. The center displays the total value. Control the hole size with data-wf-inner-radius (0–1; default 0.65). Only the first dataset is rendered.

Default (0.65)

HTML
<div data-wf-chart="doughnut"
     data-wf-height="280"
     data-wf-label="Revenue by Segment"
     data-wf-legend>
  <script type="application/json">
  {
    "labels": ["Enterprise","Mid-Market","SMB","Self-Serve"],
    "datasets": [{ "label": "Revenue", "data": [42, 28, 18, 12] }]
  }
  </script>
</div>

Thin ring (0.82)

HTML
<div data-wf-chart="doughnut"
     data-wf-inner-radius="0.82"
     …>
</div>

<!-- inner-radius: 0–1  (default 0.65)
     0   = no hole (same as pie)
     0.5 = medium ring
     0.8 = thin ring -->

Pie

Use data-wf-chart="pie" for a filled pie chart (no center hole). Hover any slice to see its label and percentage in the tooltip.

HTML
<div data-wf-chart="pie"
     data-wf-height="280"
     data-wf-label="Traffic Sources"
     data-wf-legend>
  <script type="application/json">
  {
    "labels": ["Organic","Direct","Paid","Social","Referral"],
    "datasets": [{ "label": "Sessions", "data": [38, 24, 18, 12, 8] }]
  }
  </script>
</div>

Bubble

Use data-wf-chart="bubble" for a three-dimensional scatter plot. Each data point is an object with x, y, and r (radius) values. Multiple datasets can be used to group points by category with different colors.

HTML
<div data-wf-chart="bubble"
     data-wf-height="300"
     data-wf-label="Adoption vs Value"
     data-wf-legend>
  <script type="application/json">
  {
    "labels": [],
    "datasets": [
      { "label": "Analytics", "data": [{ "x": 72, "y": 85, "r": 18 }] },
      { "label": "CRM",       "data": [{ "x": 48, "y": 62, "r": 12 }] },
      { "label": "Email",     "data": [{ "x": 91, "y": 78, "r": 22 }] },
      { "label": "Storage",   "data": [{ "x": 35, "y": 44, "r": 8  }] },
      { "label": "Video",     "data": [{ "x": 58, "y": 91, "r": 15 }] }
    ]
  }
  </script>
</div>

<!-- Each point: { "x": number, "y": number, "r": radius }
     r is scaled relative to the largest r in the dataset -->

Sparkline

Use data-wf-chart="sparkline" for a compact inline trend line. Supports the shorthand { "data": [...] } format — no labels or dataset wrapper needed. Pair with wf-stat cards to add visual trend context to a metric.

Revenue

$42,000

↗ 14% vs last month

Active Users

9,600

↗ 8% vs last month

Orders

850

↗ 9% vs last month

Bounce Rate

38.4%

↘ 4% vs last month

HTML
<div class="wf-stat">
  <p class="wf-stat-title">Revenue</p>
  <p class="wf-stat-value">$42,000</p>

  <!-- Sparkline shorthand: just a flat data array -->
  <div data-wf-chart="sparkline" data-wf-height="44" style="margin:.5rem 0">
    <script type="application/json">{"data": [18400, 21200, 19800, 24600, 26100, 42000]}</script>
  </div>

  <p class="wf-stat-desc">
    <span class="wf-stat-trend wf-stat-trend-up">↗ 14%</span> vs last month
  </p>
</div>

Synced Group

Give multiple charts the same data-wf-synced-group value to share a crosshair — hovering one chart moves the crosshair on all peers in the group. Charts must share the same number of x-axis points for the positions to align.

HTML
<!-- Both charts share the same synced-group value -->
<div data-wf-chart="area"
     data-wf-synced-group="overview"
     …>
</div>

<div data-wf-chart="line"
     data-wf-synced-group="overview"
     …>
</div>

<!-- Charts must have the same number of x-axis points -->

Live AJAX

Add data-wf-chart-target="#id" to any data-wf-ajax-action trigger. When the server response includes a chartData key the framework calls el._wfChart.update(chartData) automatically — no JS required on the page. A <select> fires on change; a button fires on click.

HTML
<!-- Period selector — fires on change, posts action=<value> -->
<select data-wf-ajax-action="getStats"
        data-wf-url="/api/api-ajax-action.php"
        data-wf-value-key="period"
        data-wf-params='{"action":"chart"}' JSON object — query params appended to data-wf-src URL.
        data-wf-chart-target="#stats-chart">
  <option value="7d">Last 7 days</option>
  <option value="30d" selected>Last 30 days</option>
  <option value="90d">Last 90 days</option>
</select>

<!-- Chart — inline initial data; AJAX replaces on period change -->
<div id="stats-chart"
     data-wf-chart="bar-multi"
     data-wf-height="280"
     data-wf-label="Revenue &amp; Orders"
     data-wf-legend
     data-wf-format="compact">
  <script type="application/json">
  {
    "labels": ["Wk 1", "Wk 2", "Wk 3", "Wk 4"],
    "datasets": [
      { "label": "Revenue", "data": [18400, 22100, 25800, 31600] },
      { "label": "Orders",  "data": [368, 442, 516, 632] }
    ]
  }
  </script>
</div>

Server response shape

The endpoint must return a standard type: "positive" response with a top-level chartData key. The value is passed verbatim to chart.update().

{
  "type": "positive",
  "chartData": {
    "labels": ["Wk 1", "Wk 2", "Wk 3", "Wk 4"],
    "datasets": [
      { "label": "Revenue", "data": [18400, 22100, 25800, 31600] },
      { "label": "Orders",  "data": [368, 442, 516, 632] }
    ]
  }
}

Custom Properties

All colors are resolved from CSS custom properties at render time. Set them on the chart element or any ancestor. Series colors default to the framework's brand palette so dark-mode switching is automatic.

Property Default Description
--wf-chart-series-1 … -13 brand palette Series stroke and fill-gradient colors. Map to --primary-color, --secondary-color, and other semantic tokens.
--wf-chart-grid var(--wf-border) Horizontal grid line color.
--wf-chart-axis var(--wf-text-muted) Axis tick label color.
--wf-chart-tooltip-bg var(--wf-surface) Tooltip background color.
--wf-chart-tooltip-border var(--wf-border) Tooltip border color.
--wf-chart-fill-opacity 0.15 Area fill gradient top-stop opacity multiplier. Increase for a more prominent fill.
--wf-chart-radius var(--wf-radius-surface) Border radius of the skeleton loader shown while remote data is loading.

Attribute Reference

Attribute Values Description
data-wf-chart area · area-multi · bar · bar-multi · bar-horizontal · line · line-multi · doughnut · pie · bubble · sparkline Required. Determines the renderer.
data-wf-height 300 SVG height in px. Width is always 100% of the container.
data-wf-label string Chart title rendered above the SVG.
data-wf-legend presence Show a series legend below the chart. Color swatches are pulled from the series palette.
data-wf-grid true Set to "false" to hide horizontal grid lines.
data-wf-x-axis true Set to "false" to hide x-axis labels.
data-wf-y-axis true Set to "false" to hide y-axis labels.
data-wf-animate true Set to "false" to disable entry animation. Respects prefers-reduced-motion.
data-wf-curve smooth · straight Line interpolation. smooth uses Catmull-Rom cubic Bézier curves.
data-wf-fill presence Force area fill on line / line-multi charts.
data-wf-stacked presence Stack series on top of each other. Requires data-wf-chart="area-multi".
data-wf-synced-group string Share a crosshair with other charts that have the same group name.
data-wf-inner-radius 0.65 Doughnut hole ratio 0–1. 0 = solid pie, 0.8 = thin ring.
data-wf-color #hex Single-series color override. Overrides --wf-chart-series-1.
data-wf-src URL GET-fetch data on init. Shows a skeleton loader while loading. Response must match the standard JSON format.
data-wf-format compact · number · currency · percent Y-axis tick label format. compact abbreviates large numbers (1K, 1M).
data-wf-currency Currency code ISO-4217 currency code, e.g. "€", "$", "¥".
Show More

Programmatic API

Access the instance via el._wfChart after the component has initialised.

const chart = document.querySelector('#my-chart')._wfChart

chart.update({ labels: [...], datasets: [...] })  // replace data and redraw
chart.reload()                                     // re-fetch data-wf-src URL
chart.destroy()                                    // remove observers and clean up

Remote data

Add data-wf-src to fetch data on init. A skeleton loader is shown while the request is in flight. The response must be a JSON object with labels and datasets keys. Charts driven by wf-ajax-action can receive live updates via data-wf-chart-target="#id" on the trigger button — the server response must include a chartData key.

<!-- Self-loading via URL -->
<div data-wf-chart="area"
     data-wf-src="/api/chart-data.php"
     data-wf-height="280">
</div>

<!-- Live update via wf-ajax-action -->
<button data-wf-action="filter"
        data-wf-chart-target="#my-chart">
  Last 30 days
</button>

<!-- Server response for ajax-action update -->
<!-- { "type": "positive", "chartData": { "labels": [...], "datasets": [...] } } -->