Chart shell
.c-chart is a dependency-free chart frame, not a charting engine. It gives
apps a consistent shell for SVG, Canvas, server-rendered images, metric headers,
legends, loading states, empty states, errors, captions, and table fallback
content.
Semantic HTML
<section
class="c-chart"
aria-labelledby="pipeline-title"
aria-describedby="pipeline-desc"
>
<header class="c-chart__header">
<div>
<h2 class="c-chart__title" id="pipeline-title">Pipeline throughput</h2>
<p class="c-chart__description" id="pipeline-desc">
Completed workflow items by day.
</p>
</div>
<div class="c-chart__metrics" aria-label="Summary metrics">
<div class="c-chart__metric">
<span class="c-chart__metric-label">Completed</span>
<strong class="c-chart__metric-value">1,284</strong>
<span class="c-chart__metric-delta" data-direction="up">+8.2%</span>
</div>
</div>
</header>
<div
class="c-chart__plot"
role="img"
aria-label="Line chart of completed items"
>
<svg viewBox="0 0 640 260" aria-hidden="true" focusable="false">
<!-- App-owned chart drawing. -->
</svg>
</div>
<ul class="c-chart__legend" aria-label="Series">
<li class="c-chart__legend-item">
<span
class="c-chart__swatch"
style="--c-chart-series-color: var(--pl-color-accent);"
></span>
Completed
</li>
</ul>
<p class="c-chart__caption">Data refreshed 2 minutes ago.</p>
<div class="c-chart__table" hidden>
<table class="c-table">
...
</table>
</div>
</section>
States
<section class="c-chart" data-state="loading" aria-busy="true">
<div class="c-chart__plot">
<div class="c-chart__overlay">
<div class="c-chart__state">
<p class="c-chart__state-title">Loading chart</p>
<p>Fetching the latest run history.</p>
</div>
</div>
</div>
</section>
<section class="c-chart" data-state="empty">...</section>
<section class="c-chart" data-state="error" role="alert">...</section>
data-state="loading", "empty", or "error" may be placed on .c-chart
or .c-chart__plot. Loading animation respects prefers-reduced-motion.
Class contract
| Selector | Role |
|---|---|
.c-chart | Chart region shell and container query root. |
.c-chart__header | Title, description, and metric strip. |
.c-chart__metrics, .c-chart__metric | Summary KPIs above or beside the plot. |
.c-chart__plot | Slot for app-owned SVG, Canvas, image, or custom chart markup. |
.c-chart__overlay, .c-chart__state | Centered loading, empty, or error message. |
.c-chart__legend | Accessible series legend. |
.c-chart__swatch | Legend color swatch; set --c-chart-series-color per item. |
.c-chart__table | Optional table fallback or source data slot. |
Accessibility responsibilities
Charts need a text alternative that matches the business question. Use
aria-labelledby and aria-describedby for the chart region, and give the plot
role="img" with a useful label when the visual itself carries meaning. Keep a
table fallback or source-data link available for complex charts. If chart data
updates live, use aria-busy during fetches and announce meaningful changes in
an app-owned live region.
SSR, hypermedia, and frameworks
The shell accepts ordinary HTML. SSR can emit a static SVG plus fallback table.
Hypermedia updates can replace only .c-chart__plot, .c-chart__metrics, or
the fallback table. Component frameworks can render Canvas or SVG inside the
plot slot without Porchlight depending on that framework.
Preview
See the chart shell preview.