Charts
Stacked bar
Bars split into up to five segments, as totals or as shares, vertical or horizontal, with a legend.
- Built on
CSS gridsubgridcontainer queries@starting-stylecolor-mix()- Client JavaScript
- None
- Works without React
- Yes, as HTML and CSS
- Source, gzipped
- 3.4 kB
- Built from
- Chart legend
Try its options
Loading its options.
Code
import { StackedBar } from "@/components/equip/stacked-bar";
const weeks = [
{ label: "Wk 41", values: [12, 31, 48] },
{ label: "Wk 42", values: [9, 36, 52] },
{ label: "Wk 43", values: [17, 28, 61] },
{ label: "Wk 44", values: [6, 33, 44] },
{ label: "Wk 45", values: [11, 40, 57] },
];
export default function Demo() {
return (
<StackedBar
label="Support tickets by priority, per week"
series={["Urgent", "High", "Normal"]}
data={weeks}
height={56}
style={{ width: 216 }}
/>
);
}Your versionchange its options in Look, This part
HTMLworks in any framework, with the part's CSS
<figure aria-label="Support tickets by priority, per week. Largest total: Wk 45, 108" class="eq-stacked-bar" data-orientation="vertical" data-totals="shown" style="width:216px;--_h:56px">
<ul role="list" aria-hidden="true" class="eq-chart-legend eq-stacked-bar-legend" data-orientation="row">
<li class="eq-chart-legend-item" data-series="0"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Urgent</span></li>
<li class="eq-chart-legend-item" data-series="1"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">High</span></li>
<li class="eq-chart-legend-item" data-series="2"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Normal</span></li>
</ul>
<ul class="eq-stacked-bar-list" role="list" aria-label="Support tickets by priority, per week">
<li class="eq-stacked-bar-item" tabindex="0" data-side="start">
<span class="eq-stacked-bar-label">Wk 41</span>
<span class="eq-stacked-bar-track" aria-hidden="true">
<span class="eq-stacked-bar-bar" style="--_v:0.8425925925925926"><span class="eq-stacked-bar-segment" data-series="0" style="flex-grow:12"></span><span class="eq-stacked-bar-segment" data-series="1" style="flex-grow:31"></span><span class="eq-stacked-bar-segment" data-series="2" style="flex-grow:48"></span></span>
</span>
<span class="eq-stacked-bar-total">91</span>
<span class="eq-stacked-bar-tip">
<span class="eq-stacked-bar-tip-label">Wk 41</span>
<ul role="list" class="eq-chart-legend" data-orientation="column">
<li class="eq-chart-legend-item" data-series="0"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Urgent</span><span class="eq-chart-legend-value">12, 13%</span></li>
<li class="eq-chart-legend-item" data-series="1"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">High</span><span class="eq-chart-legend-value">31, 34%</span></li>
<li class="eq-chart-legend-item" data-series="2"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Normal</span><span class="eq-chart-legend-value">48, 53%</span></li>
</ul>
</span>
</li>
<li class="eq-stacked-bar-item" tabindex="0" data-side="start">
<span class="eq-stacked-bar-label">Wk 42</span>
<span class="eq-stacked-bar-track" aria-hidden="true">
<span class="eq-stacked-bar-bar" style="--_v:0.8981481481481481"><span class="eq-stacked-bar-segment" data-series="0" style="flex-grow:9"></span><span class="eq-stacked-bar-segment" data-series="1" style="flex-grow:36"></span><span class="eq-stacked-bar-segment" data-series="2" style="flex-grow:52"></span></span>
</span>
<span class="eq-stacked-bar-total">97</span>
<span class="eq-stacked-bar-tip">
<span class="eq-stacked-bar-tip-label">Wk 42</span>
<ul role="list" class="eq-chart-legend" data-orientation="column">
<li class="eq-chart-legend-item" data-series="0"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Urgent</span><span class="eq-chart-legend-value">9, 9%</span></li>
<li class="eq-chart-legend-item" data-series="1"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">High</span><span class="eq-chart-legend-value">36, 37%</span></li>
<li class="eq-chart-legend-item" data-series="2"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Normal</span><span class="eq-chart-legend-value">52, 54%</span></li>
</ul>
</span>
</li>
<li class="eq-stacked-bar-item" tabindex="0" data-side="start">
<span class="eq-stacked-bar-label">Wk 43</span>
<span class="eq-stacked-bar-track" aria-hidden="true">
<span class="eq-stacked-bar-bar" style="--_v:0.9814814814814815"><span class="eq-stacked-bar-segment" data-series="0" style="flex-grow:17"></span><span class="eq-stacked-bar-segment" data-series="1" style="flex-grow:28"></span><span class="eq-stacked-bar-segment" data-series="2" style="flex-grow:61"></span></span>
</span>
<span class="eq-stacked-bar-total">106</span>
<span class="eq-stacked-bar-tip">
<span class="eq-stacked-bar-tip-label">Wk 43</span>
<ul role="list" class="eq-chart-legend" data-orientation="column">
<li class="eq-chart-legend-item" data-series="0"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Urgent</span><span class="eq-chart-legend-value">17, 16%</span></li>
<li class="eq-chart-legend-item" data-series="1"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">High</span><span class="eq-chart-legend-value">28, 26%</span></li>
<li class="eq-chart-legend-item" data-series="2"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Normal</span><span class="eq-chart-legend-value">61, 58%</span></li>
</ul>
</span>
</li>
<li class="eq-stacked-bar-item" tabindex="0" data-side="end">
<span class="eq-stacked-bar-label">Wk 44</span>
<span class="eq-stacked-bar-track" aria-hidden="true">
<span class="eq-stacked-bar-bar" style="--_v:0.7685185185185185"><span class="eq-stacked-bar-segment" data-series="0" style="flex-grow:6"></span><span class="eq-stacked-bar-segment" data-series="1" style="flex-grow:33"></span><span class="eq-stacked-bar-segment" data-series="2" style="flex-grow:44"></span></span>
</span>
<span class="eq-stacked-bar-total">83</span>
<span class="eq-stacked-bar-tip">
<span class="eq-stacked-bar-tip-label">Wk 44</span>
<ul role="list" class="eq-chart-legend" data-orientation="column">
<li class="eq-chart-legend-item" data-series="0"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Urgent</span><span class="eq-chart-legend-value">6, 7%</span></li>
<li class="eq-chart-legend-item" data-series="1"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">High</span><span class="eq-chart-legend-value">33, 40%</span></li>
<li class="eq-chart-legend-item" data-series="2"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Normal</span><span class="eq-chart-legend-value">44, 53%</span></li>
</ul>
</span>
</li>
<li class="eq-stacked-bar-item" tabindex="0" data-side="end">
<span class="eq-stacked-bar-label">Wk 45</span>
<span class="eq-stacked-bar-track" aria-hidden="true">
<span class="eq-stacked-bar-bar" style="--_v:1"><span class="eq-stacked-bar-segment" data-series="0" style="flex-grow:11"></span><span class="eq-stacked-bar-segment" data-series="1" style="flex-grow:40"></span><span class="eq-stacked-bar-segment" data-series="2" style="flex-grow:57"></span></span>
</span>
<span class="eq-stacked-bar-total">108</span>
<span class="eq-stacked-bar-tip">
<span class="eq-stacked-bar-tip-label">Wk 45</span>
<ul role="list" class="eq-chart-legend" data-orientation="column">
<li class="eq-chart-legend-item" data-series="0"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Urgent</span><span class="eq-chart-legend-value">11, 10%</span></li>
<li class="eq-chart-legend-item" data-series="1"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">High</span><span class="eq-chart-legend-value">40, 37%</span></li>
<li class="eq-chart-legend-item" data-series="2"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Normal</span><span class="eq-chart-legend-value">57, 53%</span></li>
</ul>
</span>
</li>
</ul>
</figure>Examples
Shares, horizontal
Percent mode fills every bar so shares compare across channels of very different size; the total stays by each bar.
Code
import { StackedBar } from "@/components/equip/stacked-bar";
const channels = [
{ label: "Organic search", values: [3120, 1840, 640] },
{ label: "Paid social", values: [980, 1410, 1120] },
{ label: "Email", values: [1460, 520, 110] },
{ label: "Referral", values: [610, 290, 80] },
];
export default function Example() {
return (
<StackedBar
label="Sign-ups by channel and outcome, October"
series={["Activated", "Signed up only", "Bounced"]}
data={channels}
mode="percent"
orientation="horizontal"
style={{ maxWidth: 440 }}
/>
);
}HTMLworks in any framework, with the part's CSS
<figure aria-label="Sign-ups by channel and outcome, October, as shares of each total. Largest total: Organic search, 5,600" class="eq-stacked-bar" data-orientation="horizontal" data-totals="shown" style="max-width:440px;--_h:120px">
<ul role="list" aria-hidden="true" class="eq-chart-legend eq-stacked-bar-legend" data-orientation="row">
<li class="eq-chart-legend-item" data-series="0"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Activated</span></li>
<li class="eq-chart-legend-item" data-series="1"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Signed up only</span></li>
<li class="eq-chart-legend-item" data-series="2"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Bounced</span></li>
</ul>
<ul class="eq-stacked-bar-list" role="list" aria-label="Sign-ups by channel and outcome, October">
<li class="eq-stacked-bar-item" tabindex="0" data-side="start">
<span class="eq-stacked-bar-label">Organic search</span>
<span class="eq-stacked-bar-track" aria-hidden="true">
<span class="eq-stacked-bar-bar" style="--_v:1"><span class="eq-stacked-bar-segment" data-series="0" style="flex-grow:3120"></span><span class="eq-stacked-bar-segment" data-series="1" style="flex-grow:1840"></span><span class="eq-stacked-bar-segment" data-series="2" style="flex-grow:640"></span></span>
</span>
<span class="eq-stacked-bar-total">5,600</span>
<span class="eq-stacked-bar-tip">
<span class="eq-stacked-bar-tip-label">Organic search</span>
<ul role="list" class="eq-chart-legend" data-orientation="column">
<li class="eq-chart-legend-item" data-series="0"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Activated</span><span class="eq-chart-legend-value">56%, 3,120</span></li>
<li class="eq-chart-legend-item" data-series="1"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Signed up only</span><span class="eq-chart-legend-value">33%, 1,840</span></li>
<li class="eq-chart-legend-item" data-series="2"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Bounced</span><span class="eq-chart-legend-value">11%, 640</span></li>
</ul>
</span>
</li>
<li class="eq-stacked-bar-item" tabindex="0" data-side="start">
<span class="eq-stacked-bar-label">Paid social</span>
<span class="eq-stacked-bar-track" aria-hidden="true">
<span class="eq-stacked-bar-bar" style="--_v:1"><span class="eq-stacked-bar-segment" data-series="0" style="flex-grow:980"></span><span class="eq-stacked-bar-segment" data-series="1" style="flex-grow:1410"></span><span class="eq-stacked-bar-segment" data-series="2" style="flex-grow:1120"></span></span>
</span>
<span class="eq-stacked-bar-total">3,510</span>
<span class="eq-stacked-bar-tip">
<span class="eq-stacked-bar-tip-label">Paid social</span>
<ul role="list" class="eq-chart-legend" data-orientation="column">
<li class="eq-chart-legend-item" data-series="0"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Activated</span><span class="eq-chart-legend-value">28%, 980</span></li>
<li class="eq-chart-legend-item" data-series="1"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Signed up only</span><span class="eq-chart-legend-value">40%, 1,410</span></li>
<li class="eq-chart-legend-item" data-series="2"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Bounced</span><span class="eq-chart-legend-value">32%, 1,120</span></li>
</ul>
</span>
</li>
<li class="eq-stacked-bar-item" tabindex="0" data-side="end">
<span class="eq-stacked-bar-label">Email</span>
<span class="eq-stacked-bar-track" aria-hidden="true">
<span class="eq-stacked-bar-bar" style="--_v:1"><span class="eq-stacked-bar-segment" data-series="0" style="flex-grow:1460"></span><span class="eq-stacked-bar-segment" data-series="1" style="flex-grow:520"></span><span class="eq-stacked-bar-segment" data-series="2" style="flex-grow:110"></span></span>
</span>
<span class="eq-stacked-bar-total">2,090</span>
<span class="eq-stacked-bar-tip">
<span class="eq-stacked-bar-tip-label">Email</span>
<ul role="list" class="eq-chart-legend" data-orientation="column">
<li class="eq-chart-legend-item" data-series="0"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Activated</span><span class="eq-chart-legend-value">70%, 1,460</span></li>
<li class="eq-chart-legend-item" data-series="1"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Signed up only</span><span class="eq-chart-legend-value">25%, 520</span></li>
<li class="eq-chart-legend-item" data-series="2"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Bounced</span><span class="eq-chart-legend-value">5%, 110</span></li>
</ul>
</span>
</li>
<li class="eq-stacked-bar-item" tabindex="0" data-side="end">
<span class="eq-stacked-bar-label">Referral</span>
<span class="eq-stacked-bar-track" aria-hidden="true">
<span class="eq-stacked-bar-bar" style="--_v:1"><span class="eq-stacked-bar-segment" data-series="0" style="flex-grow:610"></span><span class="eq-stacked-bar-segment" data-series="1" style="flex-grow:290"></span><span class="eq-stacked-bar-segment" data-series="2" style="flex-grow:80"></span></span>
</span>
<span class="eq-stacked-bar-total">980</span>
<span class="eq-stacked-bar-tip">
<span class="eq-stacked-bar-tip-label">Referral</span>
<ul role="list" class="eq-chart-legend" data-orientation="column">
<li class="eq-chart-legend-item" data-series="0"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Activated</span><span class="eq-chart-legend-value">62%, 610</span></li>
<li class="eq-chart-legend-item" data-series="1"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Signed up only</span><span class="eq-chart-legend-value">30%, 290</span></li>
<li class="eq-chart-legend-item" data-series="2"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Bounced</span><span class="eq-chart-legend-value">8%, 80</span></li>
</ul>
</span>
</li>
</ul>
</figure>Cloud cost by service
Up to five segments; hover or tab to a month to read each service's amount and its share of that month.
Code
import { StackedBar } from "@/components/equip/stacked-bar";
const months = [
{ label: "Jun", values: [4210, 2830, 1190, 640, 310] },
{ label: "Jul", values: [4480, 2910, 1240, 700, 290] },
{ label: "Aug", values: [4630, 3120, 1380, 720, 330] },
{ label: "Sep", values: [4920, 3340, 1310, 810, 350] },
{ label: "Oct", values: [5310, 3520, 1460, 830, 380] },
{ label: "Nov", values: [5180, 3610, 1520, 870, 410] },
];
export default function Example() {
return (
<StackedBar
label="Cloud cost by service, per month"
series={["Compute", "Database", "Storage", "Network", "Other"]}
data={months}
format={{ style: "currency", currency: "USD", notation: "compact", maximumFractionDigits: 1 }}
height={160}
style={{ maxWidth: 440 }}
/>
);
}HTMLworks in any framework, with the part's CSS
<figure aria-label="Cloud cost by service, per month. Largest total: Nov, $11.6K" class="eq-stacked-bar" data-orientation="vertical" data-totals="shown" style="max-width:440px;--_h:160px">
<ul role="list" aria-hidden="true" class="eq-chart-legend eq-stacked-bar-legend" data-orientation="row">
<li class="eq-chart-legend-item" data-series="0"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Compute</span></li>
<li class="eq-chart-legend-item" data-series="1"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Database</span></li>
<li class="eq-chart-legend-item" data-series="2"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Storage</span></li>
<li class="eq-chart-legend-item" data-series="3"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Network</span></li>
<li class="eq-chart-legend-item" data-series="4"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Other</span></li>
</ul>
<ul class="eq-stacked-bar-list" role="list" aria-label="Cloud cost by service, per month">
<li class="eq-stacked-bar-item" tabindex="0" data-side="start">
<span class="eq-stacked-bar-label">Jun</span>
<span class="eq-stacked-bar-track" aria-hidden="true">
<span class="eq-stacked-bar-bar" style="--_v:0.7920621225194133"><span class="eq-stacked-bar-segment" data-series="0" style="flex-grow:4210"></span><span class="eq-stacked-bar-segment" data-series="1" style="flex-grow:2830"></span><span class="eq-stacked-bar-segment" data-series="2" style="flex-grow:1190"></span><span class="eq-stacked-bar-segment" data-series="3" style="flex-grow:640"></span><span class="eq-stacked-bar-segment" data-series="4" style="flex-grow:310"></span></span>
</span>
<span class="eq-stacked-bar-total">$9.2K</span>
<span class="eq-stacked-bar-tip">
<span class="eq-stacked-bar-tip-label">Jun</span>
<ul role="list" class="eq-chart-legend" data-orientation="column">
<li class="eq-chart-legend-item" data-series="0"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Compute</span><span class="eq-chart-legend-value">$4.2K, 46%</span></li>
<li class="eq-chart-legend-item" data-series="1"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Database</span><span class="eq-chart-legend-value">$2.8K, 31%</span></li>
<li class="eq-chart-legend-item" data-series="2"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Storage</span><span class="eq-chart-legend-value">$1.2K, 13%</span></li>
<li class="eq-chart-legend-item" data-series="3"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Network</span><span class="eq-chart-legend-value">$640, 7%</span></li>
<li class="eq-chart-legend-item" data-series="4"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Other</span><span class="eq-chart-legend-value">$310, 3%</span></li>
</ul>
</span>
</li>
<li class="eq-stacked-bar-item" tabindex="0" data-side="start">
<span class="eq-stacked-bar-label">Jul</span>
<span class="eq-stacked-bar-track" aria-hidden="true">
<span class="eq-stacked-bar-bar" style="--_v:0.8300258843830889"><span class="eq-stacked-bar-segment" data-series="0" style="flex-grow:4480"></span><span class="eq-stacked-bar-segment" data-series="1" style="flex-grow:2910"></span><span class="eq-stacked-bar-segment" data-series="2" style="flex-grow:1240"></span><span class="eq-stacked-bar-segment" data-series="3" style="flex-grow:700"></span><span class="eq-stacked-bar-segment" data-series="4" style="flex-grow:290"></span></span>
</span>
<span class="eq-stacked-bar-total">$9.6K</span>
<span class="eq-stacked-bar-tip">
<span class="eq-stacked-bar-tip-label">Jul</span>
<ul role="list" class="eq-chart-legend" data-orientation="column">
<li class="eq-chart-legend-item" data-series="0"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Compute</span><span class="eq-chart-legend-value">$4.5K, 47%</span></li>
<li class="eq-chart-legend-item" data-series="1"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Database</span><span class="eq-chart-legend-value">$2.9K, 30%</span></li>
<li class="eq-chart-legend-item" data-series="2"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Storage</span><span class="eq-chart-legend-value">$1.2K, 13%</span></li>
<li class="eq-chart-legend-item" data-series="3"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Network</span><span class="eq-chart-legend-value">$700, 7%</span></li>
<li class="eq-chart-legend-item" data-series="4"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Other</span><span class="eq-chart-legend-value">$290, 3%</span></li>
</ul>
</span>
</li>
<li class="eq-stacked-bar-item" tabindex="0" data-side="start">
<span class="eq-stacked-bar-label">Aug</span>
<span class="eq-stacked-bar-track" aria-hidden="true">
<span class="eq-stacked-bar-bar" style="--_v:0.8783433994823123"><span class="eq-stacked-bar-segment" data-series="0" style="flex-grow:4630"></span><span class="eq-stacked-bar-segment" data-series="1" style="flex-grow:3120"></span><span class="eq-stacked-bar-segment" data-series="2" style="flex-grow:1380"></span><span class="eq-stacked-bar-segment" data-series="3" style="flex-grow:720"></span><span class="eq-stacked-bar-segment" data-series="4" style="flex-grow:330"></span></span>
</span>
<span class="eq-stacked-bar-total">$10.2K</span>
<span class="eq-stacked-bar-tip">
<span class="eq-stacked-bar-tip-label">Aug</span>
<ul role="list" class="eq-chart-legend" data-orientation="column">
<li class="eq-chart-legend-item" data-series="0"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Compute</span><span class="eq-chart-legend-value">$4.6K, 45%</span></li>
<li class="eq-chart-legend-item" data-series="1"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Database</span><span class="eq-chart-legend-value">$3.1K, 31%</span></li>
<li class="eq-chart-legend-item" data-series="2"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Storage</span><span class="eq-chart-legend-value">$1.4K, 14%</span></li>
<li class="eq-chart-legend-item" data-series="3"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Network</span><span class="eq-chart-legend-value">$720, 7%</span></li>
<li class="eq-chart-legend-item" data-series="4"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Other</span><span class="eq-chart-legend-value">$330, 3%</span></li>
</ul>
</span>
</li>
<li class="eq-stacked-bar-item" tabindex="0" data-side="end">
<span class="eq-stacked-bar-label">Sep</span>
<span class="eq-stacked-bar-track" aria-hidden="true">
<span class="eq-stacked-bar-bar" style="--_v:0.9257981018119068"><span class="eq-stacked-bar-segment" data-series="0" style="flex-grow:4920"></span><span class="eq-stacked-bar-segment" data-series="1" style="flex-grow:3340"></span><span class="eq-stacked-bar-segment" data-series="2" style="flex-grow:1310"></span><span class="eq-stacked-bar-segment" data-series="3" style="flex-grow:810"></span><span class="eq-stacked-bar-segment" data-series="4" style="flex-grow:350"></span></span>
</span>
<span class="eq-stacked-bar-total">$10.7K</span>
<span class="eq-stacked-bar-tip">
<span class="eq-stacked-bar-tip-label">Sep</span>
<ul role="list" class="eq-chart-legend" data-orientation="column">
<li class="eq-chart-legend-item" data-series="0"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Compute</span><span class="eq-chart-legend-value">$4.9K, 46%</span></li>
<li class="eq-chart-legend-item" data-series="1"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Database</span><span class="eq-chart-legend-value">$3.3K, 31%</span></li>
<li class="eq-chart-legend-item" data-series="2"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Storage</span><span class="eq-chart-legend-value">$1.3K, 12%</span></li>
<li class="eq-chart-legend-item" data-series="3"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Network</span><span class="eq-chart-legend-value">$810, 8%</span></li>
<li class="eq-chart-legend-item" data-series="4"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Other</span><span class="eq-chart-legend-value">$350, 3%</span></li>
</ul>
</span>
</li>
<li class="eq-stacked-bar-item" tabindex="0" data-side="end">
<span class="eq-stacked-bar-label">Oct</span>
<span class="eq-stacked-bar-track" aria-hidden="true">
<span class="eq-stacked-bar-bar" style="--_v:0.9922346850733391"><span class="eq-stacked-bar-segment" data-series="0" style="flex-grow:5310"></span><span class="eq-stacked-bar-segment" data-series="1" style="flex-grow:3520"></span><span class="eq-stacked-bar-segment" data-series="2" style="flex-grow:1460"></span><span class="eq-stacked-bar-segment" data-series="3" style="flex-grow:830"></span><span class="eq-stacked-bar-segment" data-series="4" style="flex-grow:380"></span></span>
</span>
<span class="eq-stacked-bar-total">$11.5K</span>
<span class="eq-stacked-bar-tip">
<span class="eq-stacked-bar-tip-label">Oct</span>
<ul role="list" class="eq-chart-legend" data-orientation="column">
<li class="eq-chart-legend-item" data-series="0"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Compute</span><span class="eq-chart-legend-value">$5.3K, 46%</span></li>
<li class="eq-chart-legend-item" data-series="1"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Database</span><span class="eq-chart-legend-value">$3.5K, 31%</span></li>
<li class="eq-chart-legend-item" data-series="2"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Storage</span><span class="eq-chart-legend-value">$1.5K, 13%</span></li>
<li class="eq-chart-legend-item" data-series="3"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Network</span><span class="eq-chart-legend-value">$830, 7%</span></li>
<li class="eq-chart-legend-item" data-series="4"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Other</span><span class="eq-chart-legend-value">$380, 3%</span></li>
</ul>
</span>
</li>
<li class="eq-stacked-bar-item" tabindex="0" data-side="end">
<span class="eq-stacked-bar-label">Nov</span>
<span class="eq-stacked-bar-track" aria-hidden="true">
<span class="eq-stacked-bar-bar" style="--_v:1"><span class="eq-stacked-bar-segment" data-series="0" style="flex-grow:5180"></span><span class="eq-stacked-bar-segment" data-series="1" style="flex-grow:3610"></span><span class="eq-stacked-bar-segment" data-series="2" style="flex-grow:1520"></span><span class="eq-stacked-bar-segment" data-series="3" style="flex-grow:870"></span><span class="eq-stacked-bar-segment" data-series="4" style="flex-grow:410"></span></span>
</span>
<span class="eq-stacked-bar-total">$11.6K</span>
<span class="eq-stacked-bar-tip">
<span class="eq-stacked-bar-tip-label">Nov</span>
<ul role="list" class="eq-chart-legend" data-orientation="column">
<li class="eq-chart-legend-item" data-series="0"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Compute</span><span class="eq-chart-legend-value">$5.2K, 45%</span></li>
<li class="eq-chart-legend-item" data-series="1"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Database</span><span class="eq-chart-legend-value">$3.6K, 31%</span></li>
<li class="eq-chart-legend-item" data-series="2"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Storage</span><span class="eq-chart-legend-value">$1.5K, 13%</span></li>
<li class="eq-chart-legend-item" data-series="3"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Network</span><span class="eq-chart-legend-value">$870, 8%</span></li>
<li class="eq-chart-legend-item" data-series="4"><span class="eq-chart-legend-mark" data-mark="square" aria-hidden="true"></span><span class="eq-chart-legend-name">Other</span><span class="eq-chart-legend-value">$410, 4%</span></li>
</ul>
</span>
</li>
</ul>
</figure>No data yet
When every value is zero the chart keeps its height and says there is no data, rather than drawing empty bars.
Code
import { StackedBar } from "@/components/equip/stacked-bar";
export default function Example() {
return (
<StackedBar
label="Refunds by reason, this week"
series={["Duplicate", "Not as described", "Other"]}
data={[
{ label: "Mon", values: [0, 0, 0] },
{ label: "Tue", values: [0, 0, 0] },
]}
height={80}
style={{ maxWidth: 320 }}
/>
);
}HTMLworks in any framework, with the part's CSS
<figure aria-label="Refunds by reason, this week: no data" class="eq-stacked-bar" data-orientation="vertical" data-totals="shown" style="max-width:320px;--_h:80px">
<p class="eq-stacked-bar-empty">No data</p>
</figure>Props
| Prop | Type | Default | What it does |
|---|---|---|---|
data | { label: string; values: number[] }[] | none | One bar per category, one value per series. Missing and negative values count as zero. |
series | string[] | none | Names the segments, bottom or start first. Up to five; past five the rest are left out. |
label | string | none | Names the figure. The largest total is added to it. |
mode | "absolute" | "percent" | "absolute" | Absolute stacks to the largest total; percent fills every bar to compare shares. |
orientation | "vertical" | "horizontal" | "vertical" | Direction the bars grow. |
showTotals | boolean | true | Shows each total by its bar. When off it is still read. |
showLegend | boolean | true | Shows the legend above the bars. |
max | number | none | Fixes the end of the scale in absolute mode. |
height | number | 120 | Plot height of a vertical chart, in pixels. |
format | Intl.NumberFormatOptions | none | How values and totals are written. |
locale | string | "en-US" | Passed to Intl.NumberFormat. |
...rest | ComponentProps<"figure"> | none | Every native figure attribute. |
Anatomy
| root | .eq-stacked-bar | A figure named with the summary; data-orientation and data-totals set the layout. |
|---|---|---|
| legend | .eq-stacked-bar-legend | A Chart legend with square marks. |
| empty | .eq-stacked-bar-empty | Shown instead of bars when every value is zero. |
| list | .eq-stacked-bar-list | |
| item | .eq-stacked-bar-item | A focusable list item holding one bar. |
| label | .eq-stacked-bar-label | |
| track | .eq-stacked-bar-track | Draws the baseline. |
| bar | .eq-stacked-bar-bar | Grows from the baseline on the spring. |
| segment | .eq-stacked-bar-segment | data-series sets its ink strength. |
| total | .eq-stacked-bar-total | |
| tip | .eq-stacked-bar-tip | The readout: visually hidden until its bar is hovered or focused. |
| tip label | .eq-stacked-bar-tip-label |
Keyboard
| Tab | Moves through the bars; each one shows and reads every segment with its value and share. |
|---|
Notes
- Segments step down in ink strength in a fixed order and sit 1px apart, and the readout names each one, so none relies on colour alone.
- Each bar is a list item holding its label, total and every segment as text, so the chart reads without the picture.
- Each bar is a tab stop, which suits a handful of categories.
- Renders on the server with no client JavaScript.
- The horizontal layout uses subgrid; where it is missing, labels, bars and totals still line up within each row but not across rows.
Source
Two files, and they are the whole part. Open either to read it, copy it straight from its bar, or add the part to your loadout and install it with everything else you picked.
components/equip/stacked-bar.tsx122 lines
import type { CSSProperties, ComponentProps } from "react";
import { ChartLegend } from "./chart-legend";
import "./stacked-bar.css";
export type StackedBarDatum = {
label: string;
/** One value per series, in the same order. Missing and negative values count as zero. */
values: number[];
};
export type StackedBarProps = ComponentProps<"figure"> & {
/** One bar per category, in display order. */
data: StackedBarDatum[];
/** Names the segments, bottom or start first. Up to five; past five, the rest are left out. */
series: string[];
/** Names the chart, such as "Tickets by priority, per week". The largest total is added to it. */
label: string;
/** Absolute stacks to the largest total; percent fills every bar and compares shares. */
mode?: "absolute" | "percent";
orientation?: "vertical" | "horizontal";
/** Shows each bar's total by it. */
showTotals?: boolean;
showLegend?: boolean;
/** Fixes the end of the scale in absolute mode. */
max?: number;
/** Height of the plot area in a vertical chart, in pixels. */
height?: number;
format?: Intl.NumberFormatOptions;
locale?: string;
};
const MAX_SERIES = 5;
export function StackedBar({
data,
series,
label,
mode = "absolute",
orientation = "vertical",
showTotals = true,
showLegend = true,
max,
height = 120,
format,
locale = "en-US",
className,
style,
...rest
}: StackedBarProps) {
const number = new Intl.NumberFormat(locale, format);
const percent = new Intl.NumberFormat(locale, { style: "percent", maximumFractionDigits: 0 });
const names = series.slice(0, MAX_SERIES);
const rows = data.map((d) => {
const values = names.map((_, k) => {
const v = d.values[k];
return typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0;
});
return { label: d.label, values, total: values.reduce((sum, v) => sum + v, 0) };
});
const biggest = rows.reduce<(typeof rows)[number] | undefined>((b, r) => (!b || r.total > b.total ? r : b), undefined);
const top = mode === "percent" ? 1 : Math.max(max ?? 0, biggest?.total ?? 0);
const empty = rows.length === 0 || names.length === 0 || !biggest || biggest.total === 0;
const summary = empty
? `${label}: no data`
: mode === "percent"
? `${label}, as shares of each total. Largest total: ${biggest.label}, ${number.format(biggest.total)}`
: `${label}. Largest total: ${biggest.label}, ${number.format(biggest.total)}`;
return (
<figure
aria-label={summary}
{...rest}
className={["eq-stacked-bar", className].filter(Boolean).join(" ")}
data-orientation={orientation}
data-totals={showTotals ? "shown" : "hidden"}
style={{ ...style, "--_h": `${height}px` } as CSSProperties}
>
{showLegend && !empty && (
<ChartLegend className="eq-stacked-bar-legend" items={names.map((name) => ({ label: name }))} aria-hidden="true" />
)}
{empty ? (
<p className="eq-stacked-bar-empty">No data</p>
) : (
<ul className="eq-stacked-bar-list" role="list" aria-label={label}>
{rows.map((r, i) => (
<li
key={`${r.label}-${i}`}
className="eq-stacked-bar-item"
tabIndex={0}
data-side={i >= rows.length / 2 ? "end" : "start"}
>
<span className="eq-stacked-bar-label">{r.label}</span>
<span className="eq-stacked-bar-track" aria-hidden="true">
<span
className="eq-stacked-bar-bar"
style={{ "--_v": mode === "percent" ? (r.total > 0 ? 1 : 0) : r.total / (top || 1) } as CSSProperties}
>
{r.values.map((v, k) =>
v > 0 ? <span key={k} className="eq-stacked-bar-segment" data-series={k} style={{ flexGrow: v }} /> : null,
)}
</span>
</span>
<span className="eq-stacked-bar-total">{number.format(r.total)}</span>
<span className="eq-stacked-bar-tip">
<span className="eq-stacked-bar-tip-label">{r.label}</span>
<ChartLegend
orientation="column"
items={names.map((name, k) => {
const v = r.values[k] ?? 0;
const share = r.total > 0 ? percent.format(v / r.total) : percent.format(0);
return { label: name, value: mode === "percent" ? `${share}, ${number.format(v)}` : `${number.format(v)}, ${share}`, series: k };
})}
/>
</span>
</li>
))}
</ul>
)}
</figure>
);
}components/equip/stacked-bar.css326 lines
@layer equip {
.eq-stacked-bar {
display: grid;
gap: var(--eq-space-3);
width: 100%;
min-width: 0;
margin: 0;
font: var(--eq-weight) var(--eq-text-xs) / 1.3 var(--eq-font);
color: var(--eq-ink-2);
}
/* The series steps, the same as Chart legend's square marks. */
.eq-stacked-bar [data-series] {
--_tone: var(--eq-chart-1);
}
.eq-stacked-bar [data-series="1"] {
--_tone: var(--eq-chart-2);
}
.eq-stacked-bar [data-series="2"] {
--_tone: var(--eq-chart-3);
}
.eq-stacked-bar [data-series="3"] {
--_tone: var(--eq-chart-4);
}
.eq-stacked-bar [data-series="4"] {
--_tone: var(--eq-chart-5);
}
.eq-stacked-bar-empty {
display: grid;
place-items: center;
min-height: var(--_h);
margin: 0;
border-block-end: 1px solid var(--eq-line-strong);
color: var(--eq-ink-3);
}
.eq-stacked-bar[data-orientation="horizontal"] .eq-stacked-bar-empty {
min-height: calc(var(--eq-space-6) * 2);
}
.eq-stacked-bar-list {
margin: 0;
padding: 0;
list-style: none;
container-type: inline-size;
}
.eq-stacked-bar[data-orientation="horizontal"] .eq-stacked-bar-list {
display: grid;
grid-template-columns: max-content minmax(0, 1fr) max-content;
gap: var(--eq-space-1) var(--eq-space-2);
}
.eq-stacked-bar[data-orientation="vertical"] .eq-stacked-bar-list {
display: grid;
grid-auto-flow: column;
grid-auto-columns: minmax(0, 1fr);
gap: var(--eq-space-2);
}
.eq-stacked-bar-item {
position: relative;
display: grid;
align-items: center;
min-height: 24px;
border-radius: var(--eq-radius-s);
outline: none;
cursor: default;
}
.eq-stacked-bar[data-orientation="horizontal"] .eq-stacked-bar-item {
grid-column: 1 / -1;
grid-template-columns: subgrid;
}
.eq-stacked-bar[data-orientation="vertical"] .eq-stacked-bar-item {
grid-template-rows: auto var(--_h) auto;
justify-items: center;
gap: var(--eq-space-1);
min-width: 0;
container-type: inline-size;
}
.eq-stacked-bar-label {
min-width: 0;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
transition: color var(--eq-dur) var(--eq-ease);
}
.eq-stacked-bar-total {
color: var(--eq-ink);
font-weight: var(--eq-weight-strong);
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
.eq-stacked-bar[data-orientation="vertical"] .eq-stacked-bar-total {
grid-row: 1;
}
.eq-stacked-bar[data-orientation="vertical"] .eq-stacked-bar-track {
grid-row: 2;
}
.eq-stacked-bar[data-orientation="vertical"] .eq-stacked-bar-label {
grid-row: 3;
}
/* A column too narrow for its total (with the gap beside it) keeps it for screen readers only;
the tooltip still gives every amount. The width is in em, so larger type steps aside sooner. */
@container (max-width: 2.7em) {
.eq-stacked-bar[data-orientation="vertical"] .eq-stacked-bar-total {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
border: 0;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
}
}
.eq-stacked-bar[data-totals="hidden"] .eq-stacked-bar-total {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
border: 0;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
}
.eq-stacked-bar-track {
position: relative;
display: block;
width: 100%;
}
.eq-stacked-bar[data-orientation="horizontal"] .eq-stacked-bar-track {
height: 14px;
}
.eq-stacked-bar[data-orientation="vertical"] .eq-stacked-bar-track {
height: 100%;
}
/* The baseline. Between bars it reaches across the gap, so it reads as one axis. */
.eq-stacked-bar-track::before {
content: "";
position: absolute;
background: var(--eq-line-strong);
}
.eq-stacked-bar[data-orientation="horizontal"] .eq-stacked-bar-track::before {
inset-block: calc(var(--eq-space-1) / -2);
inset-inline-start: 0;
width: 1px;
}
.eq-stacked-bar[data-orientation="vertical"] .eq-stacked-bar-track::before {
inset-inline: calc(var(--eq-space-2) / -2);
bottom: 0;
height: 1px;
}
/* Segments sit 1px apart, so each edge shows on any surface without a drawn border. */
.eq-stacked-bar-bar {
position: absolute;
display: flex;
gap: 1px;
overflow: hidden;
border-radius: calc(var(--eq-radius-s) / 2);
transition: scale var(--eq-dur-move) var(--eq-spring);
}
.eq-stacked-bar[data-orientation="horizontal"] .eq-stacked-bar-bar {
inset-block: 0;
inset-inline-start: 0;
width: calc(var(--_v) * 100%);
transform-origin: left center;
}
.eq-stacked-bar[data-orientation="vertical"] .eq-stacked-bar-bar {
left: 50%;
bottom: 0;
flex-direction: column-reverse;
width: min(100%, 28px);
height: calc(var(--_v) * 100%);
translate: -50% 0;
transform-origin: center bottom;
}
@starting-style {
.eq-stacked-bar[data-orientation="horizontal"] .eq-stacked-bar-bar {
scale: 0 1;
}
.eq-stacked-bar[data-orientation="vertical"] .eq-stacked-bar-bar {
scale: 1 0;
}
}
.eq-stacked-bar-segment {
flex: 1 1 0;
min-width: 0;
min-height: 0;
background: var(--_tone, var(--eq-ink));
}
/* The readout. Out of sight it is still text, so a screen reader reads it with its bar. */
.eq-stacked-bar-tip {
position: absolute;
top: 0;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
border: 0;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
}
.eq-stacked-bar-tip-label {
display: block;
margin-block-end: var(--eq-space-1);
color: var(--eq-ink);
font-weight: var(--eq-weight-strong);
}
.eq-stacked-bar-item:focus-visible {
z-index: 1;
outline: 2px solid var(--eq-focus);
outline-offset: 2px;
}
.eq-stacked-bar-item:focus-visible .eq-stacked-bar-label {
color: var(--eq-ink);
}
.eq-stacked-bar-item:focus-visible .eq-stacked-bar-tip {
z-index: 1;
width: max-content;
max-width: calc(50cqi - var(--eq-space-3));
height: auto;
margin: 0;
padding: var(--eq-space-1) var(--eq-space-2);
overflow: visible;
clip-path: none;
white-space: normal;
border: 1px solid var(--eq-line-strong);
border-radius: var(--eq-radius-m);
background: var(--eq-raised);
box-shadow: var(--eq-shadow);
-webkit-backdrop-filter: var(--eq-blur);
backdrop-filter: var(--eq-blur);
}
.eq-stacked-bar[data-orientation="vertical"] .eq-stacked-bar-item[data-side="start"]:focus-visible .eq-stacked-bar-tip {
left: calc(50% + var(--eq-space-4) + 2px);
}
.eq-stacked-bar[data-orientation="vertical"] .eq-stacked-bar-item[data-side="end"]:focus-visible .eq-stacked-bar-tip {
right: calc(50% + var(--eq-space-4) + 2px);
}
.eq-stacked-bar[data-orientation="horizontal"] .eq-stacked-bar-item:focus-visible .eq-stacked-bar-tip {
top: calc(100% + var(--eq-space-1));
left: 0;
max-width: 100cqi;
}
@media (hover: hover) {
.eq-stacked-bar-item:hover {
z-index: 1;
}
.eq-stacked-bar-item:hover .eq-stacked-bar-label {
color: var(--eq-ink);
}
.eq-stacked-bar-item:hover .eq-stacked-bar-tip {
z-index: 1;
width: max-content;
max-width: calc(50cqi - var(--eq-space-3));
height: auto;
margin: 0;
padding: var(--eq-space-1) var(--eq-space-2);
overflow: visible;
clip-path: none;
white-space: normal;
border: 1px solid var(--eq-line-strong);
border-radius: var(--eq-radius-m);
background: var(--eq-raised);
box-shadow: var(--eq-shadow);
-webkit-backdrop-filter: var(--eq-blur);
backdrop-filter: var(--eq-blur);
}
.eq-stacked-bar[data-orientation="vertical"] .eq-stacked-bar-item[data-side="start"]:hover .eq-stacked-bar-tip {
left: calc(50% + var(--eq-space-4) + 2px);
}
.eq-stacked-bar[data-orientation="vertical"] .eq-stacked-bar-item[data-side="end"]:hover .eq-stacked-bar-tip {
right: calc(50% + var(--eq-space-4) + 2px);
}
.eq-stacked-bar[data-orientation="horizontal"] .eq-stacked-bar-item:hover .eq-stacked-bar-tip {
top: calc(100% + var(--eq-space-1));
left: 0;
max-width: 100cqi;
}
}
}