md-pie-chart spec card
Identity · when to use / when NOT · decision cues · behavioural contract · do/don't · anti-patterns · full API. Paste into your agent when you're implementing with this component.
Parts of a single whole. Full pies, donuts, half-pie gauges and nested rings, with padding angles, exploded slices and a centre slot for donut KPIs. Slice colours resolve to MD3 colour roles, so dark theme and brand-token overrides re-tint automatically.
<md-pie-chart label="Traffic" legend="right" style="--md-pie-chart-aspect-ratio: auto; --md-pie-chart-block-size: 340px;"></md-pie-chart>
<script type="module">
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
},
{
label: "Email",
value: 80
}
];
const el = document.querySelector("md-pie-chart");
Object.assign(el, { data });
</script>import { MdPieChart } from '@awc-ui/react';
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
},
{
label: "Email",
value: 80
}
];
export function Chart() {
return (
<MdPieChart
data={data}
label="Traffic"
legend="right"
/>
);
}import { Component } from '@angular/core';
@Component({
selector: 'app-chart',
template: `
<md-pie-chart
[data]="data"
label="Traffic"
legend="right"
></md-pie-chart>
`,
})
export class ChartComponent {
data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
},
{
label: "Email",
value: 80
}
];
}<script setup lang="ts">
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
},
{
label: "Email",
value: 80
}
];
</script>
<template>
<md-pie-chart
:data="data"
label="Traffic"
legend="right"
/>
</template><script lang="ts">
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
},
{
label: "Email",
value: 80
}
];
</script>
<md-pie-chart
data={data}
label="Traffic"
legend="right"
/>
Already installed? See the
Installation guide for one-time package setup
(core + tokens, fonts). Each tab below shows two patterns for using
md-pie-chart in your project: Option A registers every AWC UI
component at once (simplest), Option B imports only this component for
tree-shake-friendly bundles.
<!-- ─── Option A: global registration (all components) ─── -->
<script type="module">
import '@awc-ui/core/define';
</script>
<!-- ─── Option B: single import (tree-shake only md-pie-chart) ─── -->
<script type="module">
import '@awc-ui/core/components/md-pie-chart';
</script>
<md-pie-chart>Pie Chart</md-pie-chart>// ─── Option A: typed React wrapper (registers all components) ───
// Importing from '@awc-ui/react' calls defineCustomElements() as a
// side effect, so every md-* element becomes available in the browser.
import { MdPieChart } from '@awc-ui/react';
export function Example() {
return <MdPieChart>Pie Chart</MdPieChart>;
}
// ─── Option B: single import (tree-shake to only md-pie-chart) ───
// Skip the wrapper and use the raw custom element. Smallest bundle,
// but you lose typed props/events on JSX.
import '@awc-ui/core/components/md-pie-chart';
export function ExampleTreeShaken() {
return <md-pie-chart>Pie Chart</md-pie-chart>;
}// ─── Option A: schema module (any md-* element accepted) ───
// Pair with `defineCustomElements(window)` in main.ts.
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@Component({
standalone: true,
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `<md-pie-chart>Pie Chart</md-pie-chart>`,
})
export class ExampleComponent {}
// ─── Option B: typed directive (tree-shake friendly) ───
// Pair with `import '@awc-ui/core/components/md-pie-chart'` in main.ts.
import { Component } from '@angular/core';
import { MdPieChart } from '@awc-ui/angular';
@Component({
standalone: true,
imports: [MdPieChart],
template: `<md-pie-chart>Pie Chart</md-pie-chart>`,
})
export class ExampleTreeShakenComponent {}<!-- ─── Option A: typed Vue wrapper (registers all components) ─── -->
<script setup lang="ts">
import { MdPieChart } from '@awc-ui/vue';
</script>
<template>
<MdPieChart>Pie Chart</MdPieChart>
</template>
<!-- ─── Option B: single import (tree-shake to only md-pie-chart) ─── -->
<script setup lang="ts">
import '@awc-ui/core/components/md-pie-chart';
</script>
<template>
<md-pie-chart>Pie Chart</md-pie-chart>
</template><!-- ─── Option A: global registration (done once in main entry) ─── -->
<!-- main.ts: -->
<!-- import { defineCustomElements } from '@awc-ui/svelte'; -->
<!-- defineCustomElements(window); -->
<md-pie-chart>Pie Chart</md-pie-chart>
<!-- ─── Option B: single import (tree-shake to only md-pie-chart) ─── -->
<script lang="ts">
import '@awc-ui/core/components/md-pie-chart';
</script>
<md-pie-chart>Pie Chart</md-pie-chart>| Situation | Use instead |
|---|---|
| Comparing magnitudes across categories | md-bar-chart |
| Change over time | md-line-chart |
| Composition over time | md-area-chart with stack="percentage" |
| Values that don’t sum to a meaningful whole | md-bar-chart |
| More than ~6 categories | md-bar-chart, sorted |
| Precise comparison between similar slices | md-bar-chart — angles are hard to compare |
| Exact values users must read | md-table |
| Need | Setting |
|---|---|
| Donut | inner-radius="60%" |
| Centre KPI | the center slot (donut only) |
| Half-pie gauge | start-angle="180" end-angle="0" |
| Separate the slices | padding-angle |
| Pull one slice out | selected: true on the datum |
| One-colour ramp | monochrome |
| Depth on the slices | gradient |
| Values on the slices | show-labels (plus label-mode) |
| Concentric rings | ring-widths |
| Click-through | mdSliceClick (plus drill()) |
| Async data | loading (plus loading-label) |
| Reduced motion | no-animation, or animation="none" |
<md-pie-chart legend="bottom" style="--md-pie-chart-aspect-ratio: auto; --md-pie-chart-block-size: 300px;"></md-pie-chart>
<script type="module">
const data = [
{
label: "A",
value: 320
},
{
label: "B",
value: 240
},
{
label: "C",
value: 180
},
{
label: "D",
value: 120
}
];
const el = document.querySelector("md-pie-chart");
Object.assign(el, { data });
</script>import { MdPieChart } from '@awc-ui/react';
const data = [
{
label: "A",
value: 320
},
{
label: "B",
value: 240
},
{
label: "C",
value: 180
},
{
label: "D",
value: 120
}
];
export function Chart() {
return (
<MdPieChart
data={data}
legend="bottom"
/>
);
}import { Component } from '@angular/core';
@Component({
selector: 'app-chart',
template: `
<md-pie-chart
[data]="data"
legend="bottom"
></md-pie-chart>
`,
})
export class ChartComponent {
data = [
{
label: "A",
value: 320
},
{
label: "B",
value: 240
},
{
label: "C",
value: 180
},
{
label: "D",
value: 120
}
];
}<script setup lang="ts">
const data = [
{
label: "A",
value: 320
},
{
label: "B",
value: 240
},
{
label: "C",
value: 180
},
{
label: "D",
value: 120
}
];
</script>
<template>
<md-pie-chart
:data="data"
legend="bottom"
/>
</template><script lang="ts">
const data = [
{
label: "A",
value: 320
},
{
label: "B",
value: 240
},
{
label: "C",
value: 180
},
{
label: "D",
value: 120
}
];
</script>
<md-pie-chart
data={data}
legend="bottom"
/><md-pie-chart inner-radius="60%" legend="bottom" style="--md-pie-chart-aspect-ratio: auto; --md-pie-chart-block-size: 320px;"><div slot="center">
<div style="font-size: 28px; font-weight: 600; line-height: 1;">860</div>
<div style="font-size: 12px; opacity: 0.7;">visitors</div>
</div></md-pie-chart>
<script type="module">
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
const el = document.querySelector("md-pie-chart");
Object.assign(el, { data });
</script>import { MdPieChart } from '@awc-ui/react';
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
export function Chart() {
return (
<MdPieChart
data={data}
inner-radius="60%"
legend="bottom"
/>
);
}import { Component } from '@angular/core';
@Component({
selector: 'app-chart',
template: `
<md-pie-chart
[data]="data"
inner-radius="60%"
legend="bottom"
></md-pie-chart>
`,
})
export class ChartComponent {
data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
}<script setup lang="ts">
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
</script>
<template>
<md-pie-chart
:data="data"
inner-radius="60%"
legend="bottom"
/>
</template><script lang="ts">
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
</script>
<md-pie-chart
data={data}
inner-radius="60%"
legend="bottom"
/><md-pie-chart inner-radius="60%" outer-radius="95%" start-angle="180" end-angle="0" legend="none" style="--md-pie-chart-aspect-ratio: 2 / 1;"><div slot="center" style="margin-block-start: -32px;">
<div style="font-size: 32px; font-weight: 600; line-height: 1;">68%</div>
<div style="font-size: 12px; opacity: 0.7;">storage used</div>
</div></md-pie-chart>
<script type="module">
const data = [
{
label: "Used",
value: 68,
color: "primary"
},
{
label: "Free",
value: 32,
color: "surface-variant"
}
];
const el = document.querySelector("md-pie-chart");
Object.assign(el, { data });
</script>import { MdPieChart } from '@awc-ui/react';
const data = [
{
label: "Used",
value: 68,
color: "primary"
},
{
label: "Free",
value: 32,
color: "surface-variant"
}
];
export function Chart() {
return (
<MdPieChart
data={data}
inner-radius="60%"
outer-radius="95%"
start-angle="180"
end-angle="0"
legend="none"
style="--md-pie-chart-aspect-ratio: 2 / 1;"
/>
);
}import { Component } from '@angular/core';
@Component({
selector: 'app-chart',
template: `
<md-pie-chart
[data]="data"
inner-radius="60%"
outer-radius="95%"
start-angle="180"
end-angle="0"
legend="none"
style="--md-pie-chart-aspect-ratio: 2 / 1;"
></md-pie-chart>
`,
})
export class ChartComponent {
data = [
{
label: "Used",
value: 68,
color: "primary"
},
{
label: "Free",
value: 32,
color: "surface-variant"
}
];
}<script setup lang="ts">
const data = [
{
label: "Used",
value: 68,
color: "primary"
},
{
label: "Free",
value: 32,
color: "surface-variant"
}
];
</script>
<template>
<md-pie-chart
:data="data"
inner-radius="60%"
outer-radius="95%"
start-angle="180"
end-angle="0"
legend="none"
style="--md-pie-chart-aspect-ratio: 2 / 1;"
/>
</template><script lang="ts">
const data = [
{
label: "Used",
value: 68,
color: "primary"
},
{
label: "Free",
value: 32,
color: "surface-variant"
}
];
</script>
<md-pie-chart
data={data}
inner-radius="60%"
outer-radius="95%"
start-angle="180"
end-angle="0"
legend="none"
style="--md-pie-chart-aspect-ratio: 2 / 1;"
/>Set selected: true on a datum to pull it outward — useful for “this slice matters
most” stories.
<md-pie-chart padding-angle="2" style="--md-pie-chart-aspect-ratio: auto; --md-pie-chart-block-size: 300px;"></md-pie-chart>
<script type="module">
const data = [
{
label: "Direct",
value: 320,
selected: true
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
const el = document.querySelector("md-pie-chart");
Object.assign(el, { data });
</script>import { MdPieChart } from '@awc-ui/react';
const data = [
{
label: "Direct",
value: 320,
selected: true
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
export function Chart() {
return (
<MdPieChart
data={data}
padding-angle="2"
/>
);
}import { Component } from '@angular/core';
@Component({
selector: 'app-chart',
template: `
<md-pie-chart
[data]="data"
padding-angle="2"
></md-pie-chart>
`,
})
export class ChartComponent {
data = [
{
label: "Direct",
value: 320,
selected: true
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
}<script setup lang="ts">
const data = [
{
label: "Direct",
value: 320,
selected: true
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
</script>
<template>
<md-pie-chart
:data="data"
padding-angle="2"
/>
</template><script lang="ts">
const data = [
{
label: "Direct",
value: 320,
selected: true
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
</script>
<md-pie-chart
data={data}
padding-angle="2"
/>Four numbers shape every variant. They’re independent, so a half-pie donut with separated slices is just all four set at once.
| Prop | Effect |
|---|---|
inner-radius | Size of the hole. 0 is a pie, 60% a donut |
outer-radius | Where the slices end, as a fraction of the available radius |
start-angle / end-angle | The arc the chart sweeps. 180 → 0 is a half-pie |
padding-angle | Degrees of gap between slices |
corner-radius | Rounds the slice ends |
<md-pie-chart padding-angle="4" corner-radius="6" inner-radius="45%" legend="right" style="--md-pie-chart-aspect-ratio: auto; --md-pie-chart-block-size: 300px;"></md-pie-chart>
<script type="module">
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
const el = document.querySelector("md-pie-chart");
Object.assign(el, { data });
</script>import { MdPieChart } from '@awc-ui/react';
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
export function Chart() {
return (
<MdPieChart
data={data}
padding-angle="4"
corner-radius="6"
inner-radius="45%"
legend="right"
/>
);
}import { Component } from '@angular/core';
@Component({
selector: 'app-chart',
template: `
<md-pie-chart
[data]="data"
padding-angle="4"
corner-radius="6"
inner-radius="45%"
legend="right"
></md-pie-chart>
`,
})
export class ChartComponent {
data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
}<script setup lang="ts">
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
</script>
<template>
<md-pie-chart
:data="data"
padding-angle="4"
corner-radius="6"
inner-radius="45%"
legend="right"
/>
</template><script lang="ts">
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
</script>
<md-pie-chart
data={data}
padding-angle="4"
corner-radius="6"
inner-radius="45%"
legend="right"
/>A half-pie reads as a gauge: the arc is a track, the filled part a reading. Set
the aspect ratio to 2 / 1 so the box matches the shape and the chart doesn’t
reserve empty space below.
<md-pie-chart start-angle="180" end-angle="0" inner-radius="62%" outer-radius="95%" legend="bottom" style="--md-pie-chart-aspect-ratio: 2 / 1;"></md-pie-chart>
<script type="module">
const data = [
{
label: "Complete",
value: 72,
color: "primary"
},
{
label: "Remaining",
value: 28,
color: "surface-variant"
}
];
const el = document.querySelector("md-pie-chart");
Object.assign(el, { data });
</script>import { MdPieChart } from '@awc-ui/react';
const data = [
{
label: "Complete",
value: 72,
color: "primary"
},
{
label: "Remaining",
value: 28,
color: "surface-variant"
}
];
export function Chart() {
return (
<MdPieChart
data={data}
start-angle="180"
end-angle="0"
inner-radius="62%"
outer-radius="95%"
legend="bottom"
/>
);
}import { Component } from '@angular/core';
@Component({
selector: 'app-chart',
template: `
<md-pie-chart
[data]="data"
start-angle="180"
end-angle="0"
inner-radius="62%"
outer-radius="95%"
legend="bottom"
></md-pie-chart>
`,
})
export class ChartComponent {
data = [
{
label: "Complete",
value: 72,
color: "primary"
},
{
label: "Remaining",
value: 28,
color: "surface-variant"
}
];
}<script setup lang="ts">
const data = [
{
label: "Complete",
value: 72,
color: "primary"
},
{
label: "Remaining",
value: 28,
color: "surface-variant"
}
];
</script>
<template>
<md-pie-chart
:data="data"
start-angle="180"
end-angle="0"
inner-radius="62%"
outer-radius="95%"
legend="bottom"
/>
</template><script lang="ts">
const data = [
{
label: "Complete",
value: 72,
color: "primary"
},
{
label: "Remaining",
value: 28,
color: "surface-variant"
}
];
</script>
<md-pie-chart
data={data}
start-angle="180"
end-angle="0"
inner-radius="62%"
outer-radius="95%"
legend="bottom"
/>A datum can carry its own radius (0–1), so the slice’s angle and its
length encode two different measures — area by angle, density by reach. It
is a rose / Nightingale chart, and it earns its keep only when the second
measure genuinely matters; otherwise it just looks busy.
<md-pie-chart inner-radius="12%" outer-radius="95%" legend="bottom" style="--md-pie-chart-aspect-ratio: auto; --md-pie-chart-block-size: 460px;"></md-pie-chart>
<script type="module">
const data = [
{
label: "Netherlands",
value: 41,
radius: 1
},
{
label: "Belgium",
value: 30,
radius: 0.73
},
{
label: "Germany",
value: 357,
radius: 0.53
},
{
label: "France",
value: 551,
radius: 0.29
},
{
label: "Spain",
value: 505,
radius: 0.22
},
{
label: "Sweden",
value: 450,
radius: 0.06
}
];
const el = document.querySelector("md-pie-chart");
Object.assign(el, { data });
</script>import { MdPieChart } from '@awc-ui/react';
const data = [
{
label: "Netherlands",
value: 41,
radius: 1
},
{
label: "Belgium",
value: 30,
radius: 0.73
},
{
label: "Germany",
value: 357,
radius: 0.53
},
{
label: "France",
value: 551,
radius: 0.29
},
{
label: "Spain",
value: 505,
radius: 0.22
},
{
label: "Sweden",
value: 450,
radius: 0.06
}
];
export function Chart() {
return (
<MdPieChart
data={data}
inner-radius="12%"
outer-radius="95%"
legend="bottom"
/>
);
}import { Component } from '@angular/core';
@Component({
selector: 'app-chart',
template: `
<md-pie-chart
[data]="data"
inner-radius="12%"
outer-radius="95%"
legend="bottom"
></md-pie-chart>
`,
})
export class ChartComponent {
data = [
{
label: "Netherlands",
value: 41,
radius: 1
},
{
label: "Belgium",
value: 30,
radius: 0.73
},
{
label: "Germany",
value: 357,
radius: 0.53
},
{
label: "France",
value: 551,
radius: 0.29
},
{
label: "Spain",
value: 505,
radius: 0.22
},
{
label: "Sweden",
value: 450,
radius: 0.06
}
];
}<script setup lang="ts">
const data = [
{
label: "Netherlands",
value: 41,
radius: 1
},
{
label: "Belgium",
value: 30,
radius: 0.73
},
{
label: "Germany",
value: 357,
radius: 0.53
},
{
label: "France",
value: 551,
radius: 0.29
},
{
label: "Spain",
value: 505,
radius: 0.22
},
{
label: "Sweden",
value: 450,
radius: 0.06
}
];
</script>
<template>
<md-pie-chart
:data="data"
inner-radius="12%"
outer-radius="95%"
legend="bottom"
/>
</template><script lang="ts">
const data = [
{
label: "Netherlands",
value: 41,
radius: 1
},
{
label: "Belgium",
value: 30,
radius: 0.73
},
{
label: "Germany",
value: 357,
radius: 0.53
},
{
label: "France",
value: 551,
radius: 0.29
},
{
label: "Spain",
value: 505,
radius: 0.22
},
{
label: "Sweden",
value: 450,
radius: 0.06
}
];
</script>
<md-pie-chart
data={data}
inner-radius="12%"
outer-radius="95%"
legend="bottom"
/>A slice can carry children, drawn as a second ring outside it that
subdivides exactly its own arc — so the inner ring answers which browser and
the outer one which version of it, and the two can never disagree. Nesting the
data is the API; there is no “enable rings” prop.
<md-pie-chart label="Browser market share, January 2022" subtitle="Families inside, versions outside · StatCounter" title-align="center" inner-radius="28%" outer-radius="72%" legend="none" style="--md-pie-chart-aspect-ratio: auto; --md-pie-chart-block-size: 520px;"></md-pie-chart>
<script type="module">
const data = [
{
label: "Chrome",
value: 62.74,
children: [
{
label: "v97.0",
value: 36.89
},
{
label: "v96.0",
value: 18.16
},
{
label: "v95.0",
value: 0.54
},
{
label: "Other",
value: 7.15
}
]
},
{
label: "Safari",
value: 9.86,
children: [
{
label: "v15.2",
value: 2.01
},
{
label: "v15.1",
value: 2.29
},
{
label: "v14.1",
value: 2.48
},
{
label: "v13.1",
value: 1.17
},
{
label: "Other",
value: 1.91
}
]
},
{
label: "Edge",
value: 9.17,
children: [
{
label: "v97",
value: 6.62
},
{
label: "v96",
value: 2.55
}
]
},
{
label: "Firefox",
value: 7.5,
children: [
{
label: "v96.0",
value: 4.17
},
{
label: "v95.0",
value: 3.33
}
]
},
{
label: "Other",
value: 10.73
}
];
const valueFormatter = (v) => v.toFixed(2) + '%';
const el = document.querySelector("md-pie-chart");
Object.assign(el, { data, valueFormatter });
</script>import { MdPieChart } from '@awc-ui/react';
const data = [
{
label: "Chrome",
value: 62.74,
children: [
{
label: "v97.0",
value: 36.89
},
{
label: "v96.0",
value: 18.16
},
{
label: "v95.0",
value: 0.54
},
{
label: "Other",
value: 7.15
}
]
},
{
label: "Safari",
value: 9.86,
children: [
{
label: "v15.2",
value: 2.01
},
{
label: "v15.1",
value: 2.29
},
{
label: "v14.1",
value: 2.48
},
{
label: "v13.1",
value: 1.17
},
{
label: "Other",
value: 1.91
}
]
},
{
label: "Edge",
value: 9.17,
children: [
{
label: "v97",
value: 6.62
},
{
label: "v96",
value: 2.55
}
]
},
{
label: "Firefox",
value: 7.5,
children: [
{
label: "v96.0",
value: 4.17
},
{
label: "v95.0",
value: 3.33
}
]
},
{
label: "Other",
value: 10.73
}
];
const valueFormatter = (v) => v.toFixed(2) + '%';
export function Chart() {
return (
<MdPieChart
data={data}
valueFormatter={valueFormatter}
label="Browser market share, January 2022"
subtitle="Families inside, versions outside · StatCounter"
title-align="center"
inner-radius="28%"
outer-radius="72%"
legend="none"
/>
);
}import { Component } from '@angular/core';
@Component({
selector: 'app-chart',
template: `
<md-pie-chart
[data]="data"
[valueFormatter]="valueFormatter"
label="Browser market share, January 2022"
subtitle="Families inside, versions outside · StatCounter"
title-align="center"
inner-radius="28%"
outer-radius="72%"
legend="none"
></md-pie-chart>
`,
})
export class ChartComponent {
data = [
{
label: "Chrome",
value: 62.74,
children: [
{
label: "v97.0",
value: 36.89
},
{
label: "v96.0",
value: 18.16
},
{
label: "v95.0",
value: 0.54
},
{
label: "Other",
value: 7.15
}
]
},
{
label: "Safari",
value: 9.86,
children: [
{
label: "v15.2",
value: 2.01
},
{
label: "v15.1",
value: 2.29
},
{
label: "v14.1",
value: 2.48
},
{
label: "v13.1",
value: 1.17
},
{
label: "Other",
value: 1.91
}
]
},
{
label: "Edge",
value: 9.17,
children: [
{
label: "v97",
value: 6.62
},
{
label: "v96",
value: 2.55
}
]
},
{
label: "Firefox",
value: 7.5,
children: [
{
label: "v96.0",
value: 4.17
},
{
label: "v95.0",
value: 3.33
}
]
},
{
label: "Other",
value: 10.73
}
];
valueFormatter = (v) => v.toFixed(2) + '%';
}<script setup lang="ts">
const data = [
{
label: "Chrome",
value: 62.74,
children: [
{
label: "v97.0",
value: 36.89
},
{
label: "v96.0",
value: 18.16
},
{
label: "v95.0",
value: 0.54
},
{
label: "Other",
value: 7.15
}
]
},
{
label: "Safari",
value: 9.86,
children: [
{
label: "v15.2",
value: 2.01
},
{
label: "v15.1",
value: 2.29
},
{
label: "v14.1",
value: 2.48
},
{
label: "v13.1",
value: 1.17
},
{
label: "Other",
value: 1.91
}
]
},
{
label: "Edge",
value: 9.17,
children: [
{
label: "v97",
value: 6.62
},
{
label: "v96",
value: 2.55
}
]
},
{
label: "Firefox",
value: 7.5,
children: [
{
label: "v96.0",
value: 4.17
},
{
label: "v95.0",
value: 3.33
}
]
},
{
label: "Other",
value: 10.73
}
];
const valueFormatter = (v) => v.toFixed(2) + '%';
</script>
<template>
<md-pie-chart
:data="data"
:valueFormatter="valueFormatter"
label="Browser market share, January 2022"
subtitle="Families inside, versions outside · StatCounter"
title-align="center"
inner-radius="28%"
outer-radius="72%"
legend="none"
/>
</template><script lang="ts">
const data = [
{
label: "Chrome",
value: 62.74,
children: [
{
label: "v97.0",
value: 36.89
},
{
label: "v96.0",
value: 18.16
},
{
label: "v95.0",
value: 0.54
},
{
label: "Other",
value: 7.15
}
]
},
{
label: "Safari",
value: 9.86,
children: [
{
label: "v15.2",
value: 2.01
},
{
label: "v15.1",
value: 2.29
},
{
label: "v14.1",
value: 2.48
},
{
label: "v13.1",
value: 1.17
},
{
label: "Other",
value: 1.91
}
]
},
{
label: "Edge",
value: 9.17,
children: [
{
label: "v97",
value: 6.62
},
{
label: "v96",
value: 2.55
}
]
},
{
label: "Firefox",
value: 7.5,
children: [
{
label: "v96.0",
value: 4.17
},
{
label: "v95.0",
value: 3.33
}
]
},
{
label: "Other",
value: 10.73
}
];
const valueFormatter = (v) => v.toFixed(2) + '%';
</script>
<md-pie-chart
data={data}
valueFormatter={valueFormatter}
label="Browser market share, January 2022"
subtitle="Families inside, versions outside · StatCounter"
title-align="center"
inner-radius="28%"
outer-radius="72%"
legend="none"
/>A child with no color of its own takes a lighter shade of its parent’s, so
a family reads as one block with its versions as gradations. Hovering a version
keeps its family lit too — they are the same answer, and dimming one would imply
they were unrelated.
A parent’s value is only the label; the ring geometry comes from the children,
so a child slice’s share is measured against its own level, not the whole
tree — which holds every ring at once and would count each parent twice.
Internally the tree is flattened into a plain indexed list carrying level and
parent, because every consumer-facing address is an index: tooltips, legend
toggles, mdChartClick and the screen-reader table all say slice N. Keyboard
walking follows the tree, so → from a family steps into its first
version rather than across to the next family.
ring-widths then tunes how the radius is divided between levels — one number
per ring, read as ratios. It is a property, not an attribute:
<md-pie-chart id="rings" inner-radius="28%" outer-radius="72%"></md-pie-chart>
<script type="module"> const chart = document.querySelector('#rings'); chart.ringWidths = [3, 1]; // inner band three times the outer one chart.data = [ { label: 'Chrome', value: 62.74, children: [{ label: 'v97.0', value: 36.89 }] }, ];</script>The default is deliberately not an even split: every ring but the outermost puts its labels inside its own band, so it must be wide enough to hold a word, while the outermost labels sit on leader lines and need no more than their arc.
show-labels prints on the slices; label-mode picks what: value (default),
name, or both. A slice too small to hold its label doesn’t get one rather
than spilling over its neighbours.
<md-pie-chart show-labels label-mode="both" legend="none" inner-radius="35%" style="--md-pie-chart-aspect-ratio: auto; --md-pie-chart-block-size: 320px;"></md-pie-chart>
<script type="module">
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic",
value: 240
},
{
label: "Paid",
value: 180
}
];
const el = document.querySelector("md-pie-chart");
Object.assign(el, { data });
</script>import { MdPieChart } from '@awc-ui/react';
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic",
value: 240
},
{
label: "Paid",
value: 180
}
];
export function Chart() {
return (
<MdPieChart
data={data}
show-labels
label-mode="both"
legend="none"
inner-radius="35%"
/>
);
}import { Component } from '@angular/core';
@Component({
selector: 'app-chart',
template: `
<md-pie-chart
[data]="data"
show-labels
label-mode="both"
legend="none"
inner-radius="35%"
></md-pie-chart>
`,
})
export class ChartComponent {
data = [
{
label: "Direct",
value: 320
},
{
label: "Organic",
value: 240
},
{
label: "Paid",
value: 180
}
];
}<script setup lang="ts">
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic",
value: 240
},
{
label: "Paid",
value: 180
}
];
</script>
<template>
<md-pie-chart
:data="data"
show-labels
label-mode="both"
legend="none"
inner-radius="35%"
/>
</template><script lang="ts">
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic",
value: 240
},
{
label: "Paid",
value: 180
}
];
</script>
<md-pie-chart
data={data}
show-labels
label-mode="both"
legend="none"
inner-radius="35%"
/>Each datum’s color takes an MD3 role name or any CSS colour. Without one,
slices cycle the theme palette in order.
<md-pie-chart inner-radius="55%" legend="right" style="--md-pie-chart-aspect-ratio: auto; --md-pie-chart-block-size: 320px;"></md-pie-chart>
<script type="module">
const data = [
{
label: "Primary",
value: 320,
color: "primary"
},
{
label: "Tertiary",
value: 240,
color: "tertiary"
},
{
label: "Secondary",
value: 180,
color: "secondary"
},
{
label: "Explicit hex",
value: 120,
color: "#e0b400"
}
];
const el = document.querySelector("md-pie-chart");
Object.assign(el, { data });
</script>import { MdPieChart } from '@awc-ui/react';
const data = [
{
label: "Primary",
value: 320,
color: "primary"
},
{
label: "Tertiary",
value: 240,
color: "tertiary"
},
{
label: "Secondary",
value: 180,
color: "secondary"
},
{
label: "Explicit hex",
value: 120,
color: "#e0b400"
}
];
export function Chart() {
return (
<MdPieChart
data={data}
inner-radius="55%"
legend="right"
/>
);
}import { Component } from '@angular/core';
@Component({
selector: 'app-chart',
template: `
<md-pie-chart
[data]="data"
inner-radius="55%"
legend="right"
></md-pie-chart>
`,
})
export class ChartComponent {
data = [
{
label: "Primary",
value: 320,
color: "primary"
},
{
label: "Tertiary",
value: 240,
color: "tertiary"
},
{
label: "Secondary",
value: 180,
color: "secondary"
},
{
label: "Explicit hex",
value: 120,
color: "#e0b400"
}
];
}<script setup lang="ts">
const data = [
{
label: "Primary",
value: 320,
color: "primary"
},
{
label: "Tertiary",
value: 240,
color: "tertiary"
},
{
label: "Secondary",
value: 180,
color: "secondary"
},
{
label: "Explicit hex",
value: 120,
color: "#e0b400"
}
];
</script>
<template>
<md-pie-chart
:data="data"
inner-radius="55%"
legend="right"
/>
</template><script lang="ts">
const data = [
{
label: "Primary",
value: 320,
color: "primary"
},
{
label: "Tertiary",
value: 240,
color: "tertiary"
},
{
label: "Secondary",
value: 180,
color: "secondary"
},
{
label: "Explicit hex",
value: 120,
color: "#e0b400"
}
];
</script>
<md-pie-chart
data={data}
inner-radius="55%"
legend="right"
/>monochrome ramps one hue from dark to light across the slices, in data order.
Because the ramp follows the order, it reads as a sequence — sorted shares,
stages of a funnel — and stops being arbitrary decoration.
<md-pie-chart monochrome legend="bottom" inner-radius="45%" style="--md-pie-chart-aspect-ratio: auto; --md-pie-chart-block-size: 320px;"></md-pie-chart>
<script type="module">
const data = [
{
label: "Chrome",
value: 63
},
{
label: "Safari",
value: 20
},
{
label: "Edge",
value: 9
},
{
label: "Firefox",
value: 5
},
{
label: "Other",
value: 3
}
];
const el = document.querySelector("md-pie-chart");
Object.assign(el, { data });
</script>import { MdPieChart } from '@awc-ui/react';
const data = [
{
label: "Chrome",
value: 63
},
{
label: "Safari",
value: 20
},
{
label: "Edge",
value: 9
},
{
label: "Firefox",
value: 5
},
{
label: "Other",
value: 3
}
];
export function Chart() {
return (
<MdPieChart
data={data}
monochrome
legend="bottom"
inner-radius="45%"
/>
);
}import { Component } from '@angular/core';
@Component({
selector: 'app-chart',
template: `
<md-pie-chart
[data]="data"
monochrome
legend="bottom"
inner-radius="45%"
></md-pie-chart>
`,
})
export class ChartComponent {
data = [
{
label: "Chrome",
value: 63
},
{
label: "Safari",
value: 20
},
{
label: "Edge",
value: 9
},
{
label: "Firefox",
value: 5
},
{
label: "Other",
value: 3
}
];
}<script setup lang="ts">
const data = [
{
label: "Chrome",
value: 63
},
{
label: "Safari",
value: 20
},
{
label: "Edge",
value: 9
},
{
label: "Firefox",
value: 5
},
{
label: "Other",
value: 3
}
];
</script>
<template>
<md-pie-chart
:data="data"
monochrome
legend="bottom"
inner-radius="45%"
/>
</template><script lang="ts">
const data = [
{
label: "Chrome",
value: 63
},
{
label: "Safari",
value: 20
},
{
label: "Edge",
value: 9
},
{
label: "Firefox",
value: 5
},
{
label: "Other",
value: 3
}
];
</script>
<md-pie-chart
data={data}
monochrome
legend="bottom"
inner-radius="45%"
/>gradient shades each slice from its own colour, giving the ring depth without
changing what the colours mean.
<md-pie-chart gradient inner-radius="40%" legend="right" style="--md-pie-chart-aspect-ratio: auto; --md-pie-chart-block-size: 320px;"></md-pie-chart>
<script type="module">
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
const el = document.querySelector("md-pie-chart");
Object.assign(el, { data });
</script>import { MdPieChart } from '@awc-ui/react';
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
export function Chart() {
return (
<MdPieChart
data={data}
gradient
inner-radius="40%"
legend="right"
/>
);
}import { Component } from '@angular/core';
@Component({
selector: 'app-chart',
template: `
<md-pie-chart
[data]="data"
gradient
inner-radius="40%"
legend="right"
></md-pie-chart>
`,
})
export class ChartComponent {
data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
}<script setup lang="ts">
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
</script>
<template>
<md-pie-chart
:data="data"
gradient
inner-radius="40%"
legend="right"
/>
</template><script lang="ts">
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
</script>
<md-pie-chart
data={data}
gradient
inner-radius="40%"
legend="right"
/>legend takes an anchor — top, bottom, right, left, top-end and so on
— or none. A side legend suits a pie, because the chart is square and the
gutter beside it would otherwise be empty.
<md-pie-chart legend="left" inner-radius="50%" style="--md-pie-chart-aspect-ratio: auto; --md-pie-chart-block-size: 300px;"></md-pie-chart>
<script type="module">
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
}
];
const el = document.querySelector("md-pie-chart");
Object.assign(el, { data });
</script>import { MdPieChart } from '@awc-ui/react';
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
}
];
export function Chart() {
return (
<MdPieChart
data={data}
legend="left"
inner-radius="50%"
/>
);
}import { Component } from '@angular/core';
@Component({
selector: 'app-chart',
template: `
<md-pie-chart
[data]="data"
legend="left"
inner-radius="50%"
></md-pie-chart>
`,
})
export class ChartComponent {
data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
}
];
}<script setup lang="ts">
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
}
];
</script>
<template>
<md-pie-chart
:data="data"
legend="left"
inner-radius="50%"
/>
</template><script lang="ts">
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
}
];
</script>
<md-pie-chart
data={data}
legend="left"
inner-radius="50%"
/>A donut race replays a reshuffle rather than a climb: the ring is one year’s total and the arcs are each country’s share of it. Where a line race shows values rising, this shows the composition changing — the UK holds three quarters of the total in 1965, France climbs through the 1980s, Germany’s arc closes to nothing.
The cursor is a year, not a frame index, so the slider reads as the axis it controls. Values are interpolated between yearly samples, which is what makes the arcs glide instead of stepping once a second.
drill(index) plays the transition between two levels of your own data. The
chart has no idea your slices form a hierarchy: you keep the tree, decide what
the next level is, and assign data — drill() only makes the swap read as a
descent rather than a jump cut.
Only three of the four slices lead anywhere; clicking Referral does nothing, because a slice with no children is a leaf and the handler returns early.
loading shows an opaque overlay over the plot — opaque because the engine
still draws a placeholder ring when there is no data. An empty data array
shows label-empty instead. The two never stack.
<md-pie-chart label="Traffic" loading loading-label="Fetching traffic…" style="--md-pie-chart-aspect-ratio: auto; --md-pie-chart-block-size: 260px;"></md-pie-chart>
<script type="module">
const data = [];
const el = document.querySelector("md-pie-chart");
Object.assign(el, { data });
</script>import { MdPieChart } from '@awc-ui/react';
const data = [];
export function Chart() {
return (
<MdPieChart
data={data}
label="Traffic"
loading
loading-label="Fetching traffic…"
/>
);
}import { Component } from '@angular/core';
@Component({
selector: 'app-chart',
template: `
<md-pie-chart
[data]="data"
label="Traffic"
loading
loading-label="Fetching traffic…"
></md-pie-chart>
`,
})
export class ChartComponent {
data = [];
}<script setup lang="ts">
const data = [];
</script>
<template>
<md-pie-chart
:data="data"
label="Traffic"
loading
loading-label="Fetching traffic…"
/>
</template><script lang="ts">
const data = [];
</script>
<md-pie-chart
data={data}
label="Traffic"
loading
loading-label="Fetching traffic…"
/><md-pie-chart label="Traffic" loading style="--md-pie-chart-aspect-ratio: auto; --md-pie-chart-block-size: 260px;"><div slot="loading" style="inline-size: min(320px, 70%); display: grid; gap: 12px; justify-items: center;">
<md-progress-indicator variant="linear" indeterminate label="Fetching traffic" style="inline-size: 100%;"></md-progress-indicator>
<span>Fetching traffic…</span>
</div></md-pie-chart>
<script type="module">
const data = [];
const el = document.querySelector("md-pie-chart");
Object.assign(el, { data });
</script>import { MdPieChart } from '@awc-ui/react';
const data = [];
export function Chart() {
return (
<MdPieChart
data={data}
label="Traffic"
loading
/>
);
}import { Component } from '@angular/core';
@Component({
selector: 'app-chart',
template: `
<md-pie-chart
[data]="data"
label="Traffic"
loading
></md-pie-chart>
`,
})
export class ChartComponent {
data = [];
}<script setup lang="ts">
const data = [];
</script>
<template>
<md-pie-chart
:data="data"
label="Traffic"
loading
/>
</template><script lang="ts">
const data = [];
</script>
<md-pie-chart
data={data}
label="Traffic"
loading
/><md-pie-chart label="Traffic" label-empty="No traffic recorded yet" style="--md-pie-chart-aspect-ratio: auto; --md-pie-chart-block-size: 260px;"></md-pie-chart>
<script type="module">
const data = [];
const el = document.querySelector("md-pie-chart");
Object.assign(el, { data });
</script>import { MdPieChart } from '@awc-ui/react';
const data = [];
export function Chart() {
return (
<MdPieChart
data={data}
label="Traffic"
label-empty="No traffic recorded yet"
/>
);
}import { Component } from '@angular/core';
@Component({
selector: 'app-chart',
template: `
<md-pie-chart
[data]="data"
label="Traffic"
label-empty="No traffic recorded yet"
></md-pie-chart>
`,
})
export class ChartComponent {
data = [];
}<script setup lang="ts">
const data = [];
</script>
<template>
<md-pie-chart
:data="data"
label="Traffic"
label-empty="No traffic recorded yet"
/>
</template><script lang="ts">
const data = [];
</script>
<md-pie-chart
data={data}
label="Traffic"
label-empty="No traffic recorded yet"
/>Slot your own into loader (the older name loading still works) when the
default spinner isn’t the right affordance — a circular
md-progress-indicator, a branded mark, a
skeleton.
<md-pie-chart label="Share" loading loading-label="Fetching share…"><md-progress-indicator slot="loader" variant="circular" indeterminate size="40" label="Loading"></md-progress-indicator></md-pie-chart>
<script type="module">
const data = [];
const el = document.querySelector("md-pie-chart");
Object.assign(el, { data });
</script>import { MdPieChart } from '@awc-ui/react';
const data = [];
export function Chart() {
return (
<MdPieChart
data={data}
label="Share"
loading
loading-label="Fetching share…"
/>
);
}import { Component } from '@angular/core';
@Component({
selector: 'app-chart',
template: `
<md-pie-chart
[data]="data"
label="Share"
loading
loading-label="Fetching share…"
></md-pie-chart>
`,
})
export class ChartComponent {
data = [];
}<script setup lang="ts">
const data = [];
</script>
<template>
<md-pie-chart
:data="data"
label="Share"
loading
loading-label="Fetching share…"
/>
</template><script lang="ts">
const data = [];
</script>
<md-pie-chart
data={data}
label="Share"
loading
loading-label="Fetching share…"
/>tooltip selects the trigger: item reports the slice under the pointer,
none disables pointer hover — keyboard navigation and the live region keep
working either way. A pie has no category axis, so there is no axis reading to
give: every hover is about one slice.
tooltipRenderer replaces the card’s content; the chart keeps owning the
card. Return a Node, a string (set as text), { unsafeHtml }, undefined
to fall back to the built-in card, or null to draw none. See
the area chart’s custom tooltip for the
field-by-field reference — the contract is identical across charts.
<md-pie-chart label="Traffic" inner-radius="45%" legend="right" style="--md-pie-chart-aspect-ratio: auto; --md-pie-chart-block-size: 320px;"></md-pie-chart>
<script type="module">
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
const tooltipRenderer = (ctx) => {
const el = (tag, css, text) => {
const n = document.createElement(tag);
if (css) n.style.cssText = css;
if (text != null) n.textContent = text;
return n;
};
const row = ctx.series[0];
if (!row) return undefined; // fall back to the built-in card
const card = el('div', 'min-width:170px;font-variant-numeric:tabular-nums');
card.append(el('div',
'font-size:0.68em;letter-spacing:0.1em;text-transform:uppercase;opacity:0.55',
row.label));
const head = el('div', 'display:flex;align-items:baseline;gap:5px;margin:1px 0 6px');
head.append(el('span', 'font-size:1.5em;font-weight:700;line-height:1.05', row.formattedValue));
head.append(el('span', 'font-size:0.76em;opacity:0.55', 'visits'));
card.append(head);
const swatch = el('div', 'display:grid;grid-template-columns:9px 1fr;gap:9px;align-items:center');
swatch.append(
el('span', 'width:9px;height:9px;border-radius:3px;background:' + row.color),
el('span', 'opacity:0.7', 'share of all traffic'),
);
card.append(swatch);
return card;
};
const el = document.querySelector("md-pie-chart");
Object.assign(el, { data, tooltipRenderer });
</script>import { MdPieChart } from '@awc-ui/react';
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
const tooltipRenderer = (ctx) => {
const el = (tag, css, text) => {
const n = document.createElement(tag);
if (css) n.style.cssText = css;
if (text != null) n.textContent = text;
return n;
};
const row = ctx.series[0];
if (!row) return undefined; // fall back to the built-in card
const card = el('div', 'min-width:170px;font-variant-numeric:tabular-nums');
card.append(el('div',
'font-size:0.68em;letter-spacing:0.1em;text-transform:uppercase;opacity:0.55',
row.label));
const head = el('div', 'display:flex;align-items:baseline;gap:5px;margin:1px 0 6px');
head.append(el('span', 'font-size:1.5em;font-weight:700;line-height:1.05', row.formattedValue));
head.append(el('span', 'font-size:0.76em;opacity:0.55', 'visits'));
card.append(head);
const swatch = el('div', 'display:grid;grid-template-columns:9px 1fr;gap:9px;align-items:center');
swatch.append(
el('span', 'width:9px;height:9px;border-radius:3px;background:' + row.color),
el('span', 'opacity:0.7', 'share of all traffic'),
);
card.append(swatch);
return card;
};
export function Chart() {
return (
<MdPieChart
data={data}
tooltipRenderer={tooltipRenderer}
label="Traffic"
inner-radius="45%"
legend="right"
/>
);
}import { Component } from '@angular/core';
@Component({
selector: 'app-chart',
template: `
<md-pie-chart
[data]="data"
[tooltipRenderer]="tooltipRenderer"
label="Traffic"
inner-radius="45%"
legend="right"
></md-pie-chart>
`,
})
export class ChartComponent {
data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
tooltipRenderer = (ctx) => {
const el = (tag, css, text) => {
const n = document.createElement(tag);
if (css) n.style.cssText = css;
if (text != null) n.textContent = text;
return n;
};
const row = ctx.series[0];
if (!row) return undefined; // fall back to the built-in card
const card = el('div', 'min-width:170px;font-variant-numeric:tabular-nums');
card.append(el('div',
'font-size:0.68em;letter-spacing:0.1em;text-transform:uppercase;opacity:0.55',
row.label));
const head = el('div', 'display:flex;align-items:baseline;gap:5px;margin:1px 0 6px');
head.append(el('span', 'font-size:1.5em;font-weight:700;line-height:1.05', row.formattedValue));
head.append(el('span', 'font-size:0.76em;opacity:0.55', 'visits'));
card.append(head);
const swatch = el('div', 'display:grid;grid-template-columns:9px 1fr;gap:9px;align-items:center');
swatch.append(
el('span', 'width:9px;height:9px;border-radius:3px;background:' + row.color),
el('span', 'opacity:0.7', 'share of all traffic'),
);
card.append(swatch);
return card;
};
}<script setup lang="ts">
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
const tooltipRenderer = (ctx) => {
const el = (tag, css, text) => {
const n = document.createElement(tag);
if (css) n.style.cssText = css;
if (text != null) n.textContent = text;
return n;
};
const row = ctx.series[0];
if (!row) return undefined; // fall back to the built-in card
const card = el('div', 'min-width:170px;font-variant-numeric:tabular-nums');
card.append(el('div',
'font-size:0.68em;letter-spacing:0.1em;text-transform:uppercase;opacity:0.55',
row.label));
const head = el('div', 'display:flex;align-items:baseline;gap:5px;margin:1px 0 6px');
head.append(el('span', 'font-size:1.5em;font-weight:700;line-height:1.05', row.formattedValue));
head.append(el('span', 'font-size:0.76em;opacity:0.55', 'visits'));
card.append(head);
const swatch = el('div', 'display:grid;grid-template-columns:9px 1fr;gap:9px;align-items:center');
swatch.append(
el('span', 'width:9px;height:9px;border-radius:3px;background:' + row.color),
el('span', 'opacity:0.7', 'share of all traffic'),
);
card.append(swatch);
return card;
};
</script>
<template>
<md-pie-chart
:data="data"
:tooltipRenderer="tooltipRenderer"
label="Traffic"
inner-radius="45%"
legend="right"
/>
</template><script lang="ts">
const data = [
{
label: "Direct",
value: 320
},
{
label: "Organic search",
value: 240
},
{
label: "Paid social",
value: 180
},
{
label: "Referral",
value: 120
}
];
const tooltipRenderer = (ctx) => {
const el = (tag, css, text) => {
const n = document.createElement(tag);
if (css) n.style.cssText = css;
if (text != null) n.textContent = text;
return n;
};
const row = ctx.series[0];
if (!row) return undefined; // fall back to the built-in card
const card = el('div', 'min-width:170px;font-variant-numeric:tabular-nums');
card.append(el('div',
'font-size:0.68em;letter-spacing:0.1em;text-transform:uppercase;opacity:0.55',
row.label));
const head = el('div', 'display:flex;align-items:baseline;gap:5px;margin:1px 0 6px');
head.append(el('span', 'font-size:1.5em;font-weight:700;line-height:1.05', row.formattedValue));
head.append(el('span', 'font-size:0.76em;opacity:0.55', 'visits'));
card.append(head);
const swatch = el('div', 'display:grid;grid-template-columns:9px 1fr;gap:9px;align-items:center');
swatch.append(
el('span', 'width:9px;height:9px;border-radius:3px;background:' + row.color),
el('span', 'opacity:0.7', 'share of all traffic'),
);
card.append(swatch);
return card;
};
</script>
<md-pie-chart
data={data}
tooltipRenderer={tooltipRenderer}
label="Traffic"
inner-radius="45%"
legend="right"
/>| Event | Cancelable | Detail | Fires |
|---|---|---|---|
mdSliceClick | no | MdChartClickDetail<MdPieDatum> | A slice is clicked |
mdLegendClick | no | { dataIndex, selected } | A legend chip toggles a slice |
mdReady | no | void | The engine has mounted and drawn |
MdChartClickDetail<MdPieDatum> — mdSliceClick
| Field | Type | Meaning |
|---|---|---|
dataIndex | number | Index of the slice — the value you pass to drill() |
value | number | null | Raw value of the slice |
series | MdPieDatum | The original datum you passed in |
nativeEvent | PointerEvent | MouseEvent | KeyboardEvent | A KeyboardEvent when the slice was activated from the keyboard |
| Slot | Description |
|---|---|
header | Renders above the plot (toolbar, filter chips) |
center | Centred in donut variants — perfect for KPI numbers |
empty | Empty-state content |
footer | Renders below the plot |
| Property | Attribute | Type | Default | Reflects |
|---|---|---|---|---|
label | label | string | '' | — |
titleAlign | title-align | MdChartTitleAlign | 'start' | — |
data | JS only | MdPieDatum[] | [] | — |
innerRadius | inner-radius | string | number | '0%' | — |
outerRadius | outer-radius | string | number | '75%' | — |
startAngle | start-angle | number | 90 | — |
endAngle | end-angle | number | -270 | — |
paddingAngle | padding-angle | number | 0 | — |
cornerRadius | corner-radius | number | 4 | — |
showLabels | show-labels | boolean | true | — |
labelMode | label-mode | 'value' | 'name' | 'both' | — | — |
monochrome | monochrome | string | — | — |
gradient | gradient | boolean | false | — |
highlight | highlight | 'slice' | 'series' | 'none' | 'slice' | — |
legend | legend | MdChartLegendPosition | 'none' | 'right' | Yes |
tooltip | tooltip | MdChartTooltipTrigger | 'item' | — |
locale | locale | string | '' | — |
subtitle | subtitle | string | undefined | — | — |
summary | summary | string | '' | — |
tableLabels | JS only | { category?: string | — | — |
labelEmpty | label-empty | string | 'No data to display' | — |
loading | loading | boolean | false | — |
loadingLabel | loading-label | string | 'Loading chart…' | — |
labelPlot | label-plot | string | 'Chart data. Use the arrow keys to move between slices, Home and End for the first and last, Escape to leave.' | — |
labelPoint | label-point | string | '%label%: %value% (%percent%)' | — |
tooltipRenderer | JS only | MdChartTooltipRenderer | — | — |
valueFormatter | JS only | (value: number) => string | — | — |
ringWidths | JS only | number[] | — | — |
heightProp | height | string | — | — |
noAnimation | no-animation | boolean | false | — |
animation | animation | MdChartAnimation | 'expressive' | — |
animationDuration | animation-duration | number | — | — |
density | density | 0 | -1 | -2 | -3 | -4 | 0 | Yes |
| Method | Parameters |
|---|---|
refreshTheme() | none |
resize() | none |
replay() | none |
drill() | index: number, direction: 'down' | 'up' = 'down' |
toDataURL() | none |
getInstance() | none |
| Slot | Description |
|---|---|
header | Content for the header row, replacing the default title block |
center | Centred in the donut hole — a KPI number, a label, an icon |
empty | Replaces the empty-state message when there is no data |
loader | — |
loading | Replaces the built-in spinner while `loading` is set |
footer | Content for the footer row below the plot |
Override on the host element for per-instance theming:
| Property | Description |
|---|---|
--md-pie-chart-block-size | Explicit chart height, overriding the aspect ratio (default: auto) |
--md-pie-chart-min-block-size | Floor the chart height never drops below (density-aware) |
--md-pie-chart-aspect-ratio | Width:height ratio when no block-size is set (default: 1 / 1, i.e. square) |
--md-pie-chart-background | Chart surface fill (default: surface-container-low) |
--md-pie-chart-padding | Inset between the host edge and the plot canvas (density-aware) |
--md-pie-chart-shape | Corner radius of the chart surface (density-aware) |
--md-pie-chart-empty-color | Empty-state text colour (default: on-surface-variant) |
--md-pie-chart-empty-background | Empty-state overlay fill (defaults to the chart background) |
--md-pie-chart-empty-font | Empty-state font family (default: body-medium) |
--md-pie-chart-empty-font-size | Empty-state font size (default: body-medium, 14px) |
--md-pie-chart-empty-icon-size | Size of an icon slotted into the empty state |
--md-pie-chart-center-color | Text colour of the donut centre (the `center` slot) |
Style internal elements through shadow DOM with ::part():
| Part | Description |
|---|---|
header | Header row above the plot (hosts the `header` slot) |
canvas | The plot surface the chart draws into |
center | Donut centre overlay (hosts the `center` slot) |
empty | Empty-state overlay, shown when there is no data |
loading | Loading overlay, shown while `loading` is set |
footer | Footer row below the plot (hosts the `footer` slot) |
role="figure" with an accessible name that includes the slice count.label-plot announces that on focus and
label-point templates each move.density (0 to −4) tightens padding, the legend and the label
type. It also inherits from a global data-density ancestor.locale and use Intl inside valueFormatter. Translate
label, subtitle, label-empty, label-plot and label-point;
label-point uses %x% / %values% tokens that must survive translation
verbatim, since they are replaced by a literal string match.In Storybook: dark theme and custom CSS.
| Custom property | Purpose |
|---|---|
--md-pie-chart-block-size / -min-block-size / -aspect-ratio | Chart box |
--md-pie-chart-background / -padding / -shape | Surface |
--md-pie-chart-center-color | Donut centre text |
--md-pie-chart-empty-color / -empty-background / -empty-font / -empty-font-size / -empty-icon-size | Empty state |
md-pie-chart.dashboard { --md-pie-chart-background: var(--md-sys-color-surface-container); --md-pie-chart-padding: 16px; --md-pie-chart-shape: 16px; --md-pie-chart-center-color: var(--md-sys-color-primary);}The axis-free chart still honours the family-wide
--md-chart-axis-color / --md-chart-grid-color where it draws chrome, and it
reads MD3 colour roles for the slices — so a theme switch re-tints without any
per-chart configuration.
CSS parts — header, canvas, center, empty, loading, footer, plus
tooltip (built at runtime by the engine).
md-bar-chart — ranking categoriesmd-area-chart — composition over timemd-progress-indicator — a single value, without a chartmd-pie-chartTwo artefacts to give your AI agent so it generates correct UI with this component. The per-component spec answers "how do I use this exact tag?". The main-llm spec answers "which tag should I pick in the first place?".
md-pie-chart spec card
Identity · when to use / when NOT · decision cues · behavioural contract · do/don't · anti-patterns · full API. Paste into your agent when you're implementing with this component.
System-prompt preamble · decision matrix · token reference · page recipes · anti-patterns. Paste into the system prompt at the start of a piece of work.