Across 6 layer files: 1px→var(--border-width), 1rem→var(--space-4), 2rem→var(--space-6), etc. Intentionally left: 3px accent borders, graduated chart spacing, em-based sub/sup ratios.
761 lines
28 KiB
CSS
761 lines
28 KiB
CSS
/**
|
||
* 06-charts.css
|
||
* Data-driven charts from semantic HTML tables.
|
||
* Absorbed from Charts.css — class API converted to data-attributes.
|
||
*
|
||
* Core vocabulary:
|
||
* data-chart="bar|column|line|area|pie" — chart type
|
||
* data-chart-labels — show axis labels (thead)
|
||
* data-chart-spacing="1–5" — gap between bars (default 2)
|
||
* data-chart-stacked — stacked multi-dataset mode
|
||
* style="--size: 0.8" — data injection on <td> (legal exception)
|
||
* style="--color: #hex" — per-row color override on <tr>
|
||
*
|
||
* Pragmatic exception: style="--size: N" and style="--color: X" on table cells
|
||
* are DATA injection, not presentation — they bind numeric values to CSS.
|
||
* This is the one place ASW permits inline style attributes.
|
||
*
|
||
* Chart dimensions:
|
||
* --chart-height Bar chart: bar thickness. Column chart: chart height.
|
||
* --chart-bar-size Column chart: bar width.
|
||
* --chart-gap Gap between data points.
|
||
*
|
||
* Lineage: Charts.css (MIT) — converted class API to data-attribute API.
|
||
* Reference: chartscss.org
|
||
*/
|
||
|
||
@layer charts {
|
||
|
||
/* ══════════════════════════════════════════════════════════════════════════
|
||
Shared chart tokens
|
||
══════════════════════════════════════════════════════════════════════════ */
|
||
|
||
[data-chart] {
|
||
|
||
/* Data series colors — cycle via nth-child in each chart type */
|
||
--chart-color-1: var(--accent); /* green */
|
||
--chart-color-2: var(--accent-blue); /* blue */
|
||
--chart-color-3: var(--accent-orange); /* orange */
|
||
--chart-color-4: var(--accent-red); /* red */
|
||
--chart-color-5: var(--purple-5, #ae3ec9);
|
||
--chart-color-6: var(--cyan-5, #15aabf);
|
||
--chart-color-7: var(--pink-5, #e64980);
|
||
--chart-color-8: var(--teal-5, #0ca678);
|
||
|
||
/* Layout */
|
||
--chart-height: 200px; /* column chart area height */
|
||
--chart-bar-size: var(--space-6); /* column bar width / bar chart bar height */
|
||
--chart-gap: 6px; /* spacing between data points */
|
||
|
||
/* Axis / labels */
|
||
--chart-axis: var(--border);
|
||
--chart-axis-width: var(--outline-width);
|
||
--chart-label: var(--text-3);
|
||
--chart-label-size: var(--text-xs);
|
||
|
||
/* Bar styling */
|
||
--chart-radius: var(--radius-2);
|
||
|
||
/* Reset table styles — <table> is presentational structure here */
|
||
display: block;
|
||
inline-size: 100%;
|
||
border-collapse: collapse;
|
||
border-spacing: 0;
|
||
background: transparent;
|
||
}
|
||
|
||
[data-chart] caption {
|
||
display: block;
|
||
font-size: var(--text-sm);
|
||
color: var(--text-3);
|
||
text-align: start;
|
||
padding-block-end: var(--size-3);
|
||
caption-side: top;
|
||
}
|
||
|
||
/* thead: hidden by default, shown with data-chart-labels */
|
||
[data-chart] thead {
|
||
display: none;
|
||
}
|
||
|
||
[data-chart][data-chart-labels] thead {
|
||
display: block;
|
||
}
|
||
|
||
/* tbody: each chart type overrides this */
|
||
[data-chart] tbody {
|
||
display: block;
|
||
}
|
||
|
||
/* ══════════════════════════════════════════════════════════════════════════
|
||
Bar chart — horizontal bars
|
||
══════════════════════════════════════════════════════════════════════════
|
||
|
||
Structure:
|
||
<table data-chart="bar">
|
||
<caption>Title</caption>
|
||
<tbody>
|
||
<tr>
|
||
<th scope="row">Label</th>
|
||
<td style="--size: 0.8">80%</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
The bar width = 100% × --size. Bar is a ::before pseudo on td.
|
||
══════════════════════════════════════════════════════════════════════════ */
|
||
|
||
[data-chart="bar"] tbody {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--chart-gap);
|
||
|
||
/* Left axis line */
|
||
border-inline-start: var(--chart-axis-width) solid var(--chart-axis);
|
||
padding-inline-start: 0;
|
||
}
|
||
|
||
[data-chart="bar"] tr {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--size-3);
|
||
}
|
||
|
||
/* Row label (th) */
|
||
[data-chart="bar"] th[scope="row"] {
|
||
font-size: var(--chart-label-size);
|
||
font-weight: 400;
|
||
color: var(--chart-label);
|
||
min-inline-size: 5rem;
|
||
max-inline-size: 8rem;
|
||
text-align: end;
|
||
padding-block: 0;
|
||
padding-inline: var(--size-2) 0;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* Data cell — the track */
|
||
[data-chart="bar"] td {
|
||
flex: 1;
|
||
position: relative;
|
||
block-size: var(--chart-bar-size);
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 0;
|
||
overflow: visible;
|
||
}
|
||
|
||
/* The bar itself — ::before */
|
||
[data-chart="bar"] td::before {
|
||
content: "";
|
||
display: block;
|
||
block-size: 100%;
|
||
inline-size: calc(100% * var(--size, 0.5));
|
||
background: var(--color, var(--chart-color-1));
|
||
border-radius: 0 var(--chart-radius) var(--chart-radius) 0;
|
||
transition: opacity var(--ease), inline-size var(--duration-moderate-1) var(--ease-3, ease-out);
|
||
}
|
||
|
||
[data-chart="bar"] td:hover::before {
|
||
opacity: 0.8;
|
||
}
|
||
|
||
/* Data label (text inside/after bar) */
|
||
[data-chart="bar"] td::after {
|
||
content: attr(data-value);
|
||
position: absolute;
|
||
inset-inline-start: calc(100% * var(--size, 0.5) + 0.35rem);
|
||
font-size: var(--chart-label-size);
|
||
color: var(--text-3);
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* Color cycling for multi-series */
|
||
[data-chart="bar"] tr:nth-child(1) td::before { background: var(--color, var(--chart-color-1)); }
|
||
[data-chart="bar"] tr:nth-child(2) td::before { background: var(--color, var(--chart-color-2)); }
|
||
[data-chart="bar"] tr:nth-child(3) td::before { background: var(--color, var(--chart-color-3)); }
|
||
[data-chart="bar"] tr:nth-child(4) td::before { background: var(--color, var(--chart-color-4)); }
|
||
[data-chart="bar"] tr:nth-child(5) td::before { background: var(--color, var(--chart-color-5)); }
|
||
[data-chart="bar"] tr:nth-child(6) td::before { background: var(--color, var(--chart-color-6)); }
|
||
[data-chart="bar"] tr:nth-child(7) td::before { background: var(--color, var(--chart-color-7)); }
|
||
[data-chart="bar"] tr:nth-child(8) td::before { background: var(--color, var(--chart-color-8)); }
|
||
[data-chart="bar"] tr:nth-child(n+9) td::before { background: var(--color, var(--chart-color-1)); }
|
||
|
||
/* ── Spacing modifiers ──────────────────────────────────── */
|
||
[data-chart="bar"][data-chart-spacing="1"] tbody { gap: 2px; }
|
||
[data-chart="bar"][data-chart-spacing="2"] tbody { gap: 6px; }
|
||
[data-chart="bar"][data-chart-spacing="3"] tbody { gap: 10px; }
|
||
[data-chart="bar"][data-chart-spacing="4"] tbody { gap: 16px; }
|
||
[data-chart="bar"][data-chart-spacing="5"] tbody { gap: 24px; }
|
||
|
||
/* ══════════════════════════════════════════════════════════════════════════
|
||
Column chart — vertical bars
|
||
══════════════════════════════════════════════════════════════════════════
|
||
|
||
Structure:
|
||
<table data-chart="column">
|
||
<caption>Title</caption>
|
||
<tbody>
|
||
<tr>
|
||
<th scope="row">Jan</th>
|
||
<td style="--size: 0.6">60</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
The chart area is --chart-height. Each column height = --chart-height × --size.
|
||
Columns sit at the bottom of the chart area (flex-end alignment).
|
||
══════════════════════════════════════════════════════════════════════════ */
|
||
|
||
[data-chart="column"] tbody {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: flex-end;
|
||
gap: var(--chart-gap);
|
||
block-size: var(--chart-height);
|
||
border-block-end: var(--chart-axis-width) solid var(--chart-axis);
|
||
padding: 0;
|
||
}
|
||
|
||
[data-chart="column"] tr {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: flex-end;
|
||
flex: 1;
|
||
block-size: 100%;
|
||
gap: var(--size-1);
|
||
}
|
||
|
||
/* Column label (th) at the bottom */
|
||
[data-chart="column"] th[scope="row"] {
|
||
font-size: var(--chart-label-size);
|
||
font-weight: 400;
|
||
color: var(--chart-label);
|
||
text-align: center;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
max-inline-size: 100%;
|
||
padding: 0;
|
||
padding-block-start: var(--size-1);
|
||
/* Move below axis */
|
||
order: 2;
|
||
margin-block-start: var(--size-2);
|
||
}
|
||
|
||
/* Data cell — the column bar */
|
||
[data-chart="column"] td {
|
||
display: block;
|
||
inline-size: 100%;
|
||
block-size: calc(var(--chart-height) * var(--size, 0.5));
|
||
padding: 0;
|
||
order: 1;
|
||
transition: block-size var(--duration-moderate-1) var(--ease-3, ease-out);
|
||
border-radius: var(--chart-radius) var(--chart-radius) 0 0;
|
||
}
|
||
|
||
/* Color cycling for columns */
|
||
[data-chart="column"] tr:nth-child(1) td { background: var(--color, var(--chart-color-1)); }
|
||
[data-chart="column"] tr:nth-child(2) td { background: var(--color, var(--chart-color-2)); }
|
||
[data-chart="column"] tr:nth-child(3) td { background: var(--color, var(--chart-color-3)); }
|
||
[data-chart="column"] tr:nth-child(4) td { background: var(--color, var(--chart-color-4)); }
|
||
[data-chart="column"] tr:nth-child(5) td { background: var(--color, var(--chart-color-5)); }
|
||
[data-chart="column"] tr:nth-child(6) td { background: var(--color, var(--chart-color-6)); }
|
||
[data-chart="column"] tr:nth-child(7) td { background: var(--color, var(--chart-color-7)); }
|
||
[data-chart="column"] tr:nth-child(8) td { background: var(--color, var(--chart-color-8)); }
|
||
[data-chart="column"] tr:nth-child(n+9) td { background: var(--color, var(--chart-color-1)); }
|
||
|
||
[data-chart="column"] td:hover {
|
||
opacity: 0.8;
|
||
}
|
||
|
||
/* ── Spacing modifiers ──────────────────────────────────── */
|
||
[data-chart="column"][data-chart-spacing="1"] tbody { gap: 2px; }
|
||
[data-chart="column"][data-chart-spacing="2"] tbody { gap: 6px; }
|
||
[data-chart="column"][data-chart-spacing="3"] tbody { gap: 12px; }
|
||
[data-chart="column"][data-chart-spacing="4"] tbody { gap: 20px; }
|
||
[data-chart="column"][data-chart-spacing="5"] tbody { gap: 32px; }
|
||
|
||
/* ── Column chart labels ───────────────────────────────── */
|
||
/* When data-chart-labels present, show thead as axis header */
|
||
[data-chart="column"][data-chart-labels] thead {
|
||
display: flex;
|
||
justify-content: space-around;
|
||
margin-block-end: var(--size-2);
|
||
}
|
||
|
||
[data-chart="column"][data-chart-labels] thead th {
|
||
font-size: var(--chart-label-size);
|
||
color: var(--chart-label);
|
||
font-weight: 400;
|
||
text-align: center;
|
||
flex: 1;
|
||
}
|
||
|
||
/* ══════════════════════════════════════════════════════════════════════════
|
||
Area chart — filled area from baseline
|
||
══════════════════════════════════════════════════════════════════════════
|
||
|
||
CSS-only area charts use linear-gradient on the td background.
|
||
Each point's area = --size fraction of the column height.
|
||
|
||
Structure identical to column — but cells connect visually.
|
||
The visual connection requires identical widths and no gap (or clip).
|
||
══════════════════════════════════════════════════════════════════════════ */
|
||
|
||
[data-chart="area"] tbody {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: flex-end;
|
||
block-size: var(--chart-height);
|
||
border-block-end: var(--chart-axis-width) solid var(--chart-axis);
|
||
gap: 0; /* no gap — cells must connect */
|
||
padding: 0;
|
||
}
|
||
|
||
[data-chart="area"] tr {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
justify-content: flex-end;
|
||
flex: 1;
|
||
block-size: 100%;
|
||
}
|
||
|
||
[data-chart="area"] th[scope="row"] {
|
||
font-size: var(--chart-label-size);
|
||
font-weight: 400;
|
||
color: var(--chart-label);
|
||
text-align: center;
|
||
order: 2;
|
||
padding-block-start: var(--size-1);
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
/* Area cell — filled gradient from --size down to baseline */
|
||
[data-chart="area"] td {
|
||
display: block;
|
||
inline-size: 100%;
|
||
block-size: calc(var(--chart-height) * var(--size, 0.5));
|
||
padding: 0;
|
||
order: 1;
|
||
background: linear-gradient(
|
||
to bottom,
|
||
var(--chart-color-1) 0%,
|
||
color-mix(in oklch, var(--chart-color-1), transparent 70%) 100%
|
||
);
|
||
}
|
||
|
||
/* ══════════════════════════════════════════════════════════════════════════
|
||
Line chart — dots connected by a visual line
|
||
══════════════════════════════════════════════════════════════════════════
|
||
|
||
CSS-only: we use the column approach but mark the top with a dot (::after)
|
||
and use a border-top line to simulate connection between points.
|
||
True line interpolation requires JavaScript or SVG.
|
||
|
||
What we ship: column bars in outline/transparent mode with an accent dot
|
||
at the top — semantic, readable, no JS.
|
||
══════════════════════════════════════════════════════════════════════════ */
|
||
|
||
[data-chart="line"] tbody {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: flex-end;
|
||
block-size: var(--chart-height);
|
||
border-block-end: var(--chart-axis-width) solid var(--chart-axis);
|
||
gap: 0;
|
||
padding: 0;
|
||
position: relative;
|
||
}
|
||
|
||
[data-chart="line"] tr {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: flex-end;
|
||
flex: 1;
|
||
block-size: 100%;
|
||
}
|
||
|
||
[data-chart="line"] th[scope="row"] {
|
||
font-size: var(--chart-label-size);
|
||
font-weight: 400;
|
||
color: var(--chart-label);
|
||
text-align: center;
|
||
order: 2;
|
||
padding-block-start: var(--size-1);
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* Line chart cell — transparent bar with accent top border + dot */
|
||
[data-chart="line"] td {
|
||
display: block;
|
||
inline-size: 100%;
|
||
block-size: calc(var(--chart-height) * var(--size, 0.5));
|
||
padding: 0;
|
||
order: 1;
|
||
background: linear-gradient(
|
||
to bottom,
|
||
color-mix(in oklch, var(--chart-color-1), transparent 80%) 0%,
|
||
transparent 60%
|
||
);
|
||
border-block-start: var(--outline-width) solid var(--chart-color-1);
|
||
position: relative;
|
||
}
|
||
|
||
/* Dot at data point */
|
||
[data-chart="line"] td::before {
|
||
content: "";
|
||
display: block;
|
||
position: absolute;
|
||
inset-block-start: -5px;
|
||
inset-inline-start: 50%;
|
||
translate: -50% 0;
|
||
inline-size: 8px;
|
||
block-size: 8px;
|
||
border-radius: 50%;
|
||
background: var(--chart-color-1);
|
||
border: var(--outline-width) solid var(--surface);
|
||
z-index: 1;
|
||
}
|
||
|
||
/* ══════════════════════════════════════════════════════════════════════════
|
||
Pie chart — conic-gradient segments
|
||
══════════════════════════════════════════════════════════════════════════
|
||
|
||
CSS-only pie charts use conic-gradient on a single element.
|
||
Each segment's arc = --size × 360deg.
|
||
Requires stacking values in CSS — not practical to automate per-row.
|
||
|
||
For agent use: pie charts work best with explicit conic-gradient
|
||
set as a custom property. The data-chart="pie" wrapper provides
|
||
the shape and size; the agent sets --pie-segments.
|
||
══════════════════════════════════════════════════════════════════════════ */
|
||
|
||
[data-chart="pie"] {
|
||
--pie-size: min(200px, 100%);
|
||
--pie-segments: conic-gradient(
|
||
var(--chart-color-1) 0% 25%,
|
||
var(--chart-color-2) 25% 50%,
|
||
var(--chart-color-3) 50% 75%,
|
||
var(--chart-color-4) 75% 100%
|
||
);
|
||
}
|
||
|
||
/* Pie uses a generated element — hide table structure visually */
|
||
[data-chart="pie"] tbody { display: none; }
|
||
|
||
/* Show caption + legend from thead */
|
||
[data-chart="pie"] thead {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: var(--size-2);
|
||
justify-content: center;
|
||
margin-block-end: var(--size-3);
|
||
}
|
||
|
||
[data-chart="pie"] thead th {
|
||
font-size: var(--chart-label-size);
|
||
color: var(--chart-label);
|
||
font-weight: 400;
|
||
}
|
||
|
||
/* The pie rendered as ::before on the table element */
|
||
[data-chart="pie"]::before {
|
||
content: "";
|
||
display: block;
|
||
inline-size: var(--pie-size);
|
||
block-size: var(--pie-size);
|
||
border-radius: 50%;
|
||
background: var(--pie-segments);
|
||
margin-inline: auto;
|
||
}
|
||
|
||
/* ══════════════════════════════════════════════════════════════════════════
|
||
Stacked bars — data-chart-stacked modifier
|
||
══════════════════════════════════════════════════════════════════════════
|
||
When multiple <td> in one <tr>, stack them.
|
||
══════════════════════════════════════════════════════════════════════════ */
|
||
|
||
[data-chart="bar"][data-chart-stacked] td {
|
||
/* Multiple tds per row — share the bar track inline */
|
||
display: inline-block;
|
||
inline-size: calc(100% * var(--size, 0.2));
|
||
border-radius: 0;
|
||
}
|
||
|
||
[data-chart="bar"][data-chart-stacked] td::before {
|
||
display: none; /* td IS the bar in stacked mode */
|
||
}
|
||
|
||
[data-chart="bar"][data-chart-stacked] td:first-of-type {
|
||
border-radius: 0 0 0 0;
|
||
}
|
||
|
||
[data-chart="bar"][data-chart-stacked] td:last-of-type {
|
||
border-radius: 0 var(--chart-radius) var(--chart-radius) 0;
|
||
}
|
||
|
||
/* Stacked color cycling */
|
||
[data-chart][data-chart-stacked] td:nth-of-type(1) { background: var(--chart-color-1); }
|
||
[data-chart][data-chart-stacked] td:nth-of-type(2) { background: var(--chart-color-2); }
|
||
[data-chart][data-chart-stacked] td:nth-of-type(3) { background: var(--chart-color-3); }
|
||
[data-chart][data-chart-stacked] td:nth-of-type(4) { background: var(--chart-color-4); }
|
||
|
||
/* ══════════════════════════════════════════════════════════════════════════
|
||
Accessibility
|
||
══════════════════════════════════════════════════════════════════════════ */
|
||
|
||
/* Ensure cell content (the data value) is readable for screen readers
|
||
but visually hidden inside the bar — text is in aria / caption */
|
||
[data-chart="bar"] td,
|
||
[data-chart="column"] td {
|
||
font-size: var(--chart-label-size);
|
||
color: transparent; /* data visible to SR, hidden visually */
|
||
}
|
||
|
||
/* Respect user preference — no transitions */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
[data-chart] td,
|
||
[data-chart] td::before {
|
||
transition: none;
|
||
}
|
||
}
|
||
|
||
/* ══════════════════════════════════════════════════════════════════════════
|
||
Radial chart — circular gauge
|
||
══════════════════════════════════════════════════════════════════════════
|
||
|
||
Structure:
|
||
<table data-chart="radial" style="--size: 0.72">
|
||
<caption>Token budget used</caption>
|
||
<tbody><tr><td><span>72%</span></td></tr></tbody>
|
||
</table>
|
||
|
||
The gauge is a conic-gradient on the td element.
|
||
--size (0–1) drives the arc: --size × 360deg = colored portion.
|
||
::before pseudo creates a donut hole cutout over the gradient.
|
||
<span> inside td floats the value text above the donut via z-index.
|
||
══════════════════════════════════════════════════════════════════════════ */
|
||
|
||
[data-chart="radial"] {
|
||
display: inline-flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: var(--size-2);
|
||
}
|
||
|
||
[data-chart="radial"] caption {
|
||
font-size: var(--chart-label-size);
|
||
color: var(--chart-label);
|
||
text-align: center;
|
||
caption-side: bottom;
|
||
padding-block-start: var(--size-2);
|
||
}
|
||
|
||
[data-chart="radial"] tbody { display: flex; }
|
||
[data-chart="radial"] tr { display: flex; }
|
||
|
||
/* The gauge circle */
|
||
[data-chart="radial"] td {
|
||
position: relative;
|
||
width: var(--chart-radial-size);
|
||
height: var(--chart-radial-size);
|
||
border-radius: 50%;
|
||
background: conic-gradient(
|
||
var(--color, var(--chart-color-1)) 0deg calc(var(--size, 0.5) * 360deg),
|
||
var(--surface-1, var(--gray-15)) 0deg
|
||
);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 0;
|
||
border: none;
|
||
color: transparent; /* data readable by SR, hidden visually */
|
||
}
|
||
|
||
/* Donut hole */
|
||
[data-chart="radial"] td::before {
|
||
content: "";
|
||
position: absolute;
|
||
inset: var(--chart-radial-inset);
|
||
border-radius: 50%;
|
||
background: var(--surface);
|
||
z-index: 0;
|
||
}
|
||
|
||
/* Value text centered in the donut hole */
|
||
[data-chart="radial"] td span {
|
||
position: relative;
|
||
z-index: 1;
|
||
font-size: var(--text-xs);
|
||
font-family: var(--font-mono);
|
||
color: var(--text);
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* Status color variants */
|
||
[data-chart="radial"][data-status="warning"] td {
|
||
background: conic-gradient(
|
||
var(--accent-orange, #f08c00) 0deg calc(var(--size, 0.5) * 360deg),
|
||
var(--surface-1, #111111) 0deg
|
||
);
|
||
}
|
||
|
||
[data-chart="radial"][data-status="danger"] td {
|
||
background: conic-gradient(
|
||
var(--accent-red, #e03131) 0deg calc(var(--size, 0.5) * 360deg),
|
||
var(--surface-1, #111111) 0deg
|
||
);
|
||
}
|
||
|
||
/* ══════════════════════════════════════════════════════════════════════════
|
||
Burndown chart — sprint burndown with CSS ideal-line overlay
|
||
══════════════════════════════════════════════════════════════════════════
|
||
|
||
Structure: same as column chart, but:
|
||
- Bars use --accent-red (remaining work = red)
|
||
- tbody::after renders a diagonal linear-gradient as the ideal-line
|
||
- Ideal line runs top-left to bottom-right: full work at start → zero at end
|
||
|
||
<table data-chart="burndown">
|
||
<caption>Sprint burndown</caption>
|
||
<tbody>
|
||
<tr><th scope="row">D1</th><td style="--size: 0.95">19</td></tr>
|
||
...
|
||
</tbody>
|
||
</table>
|
||
══════════════════════════════════════════════════════════════════════════ */
|
||
|
||
[data-chart="burndown"] tbody {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: flex-end;
|
||
block-size: var(--chart-height);
|
||
border-block-end: var(--chart-axis-width) solid var(--chart-axis);
|
||
position: relative; /* required for ::after overlay */
|
||
gap: var(--chart-gap);
|
||
padding: 0;
|
||
}
|
||
|
||
/* Ideal-line overlay — diagonal gradient = ideal burn velocity */
|
||
[data-chart="burndown"] tbody::after {
|
||
content: "";
|
||
position: absolute;
|
||
inset: 0;
|
||
background: linear-gradient(
|
||
to bottom right,
|
||
color-mix(in oklch, var(--chart-color-2, var(--accent-blue, #4dabf7)), transparent 20%) 0%,
|
||
transparent 100%
|
||
);
|
||
pointer-events: none;
|
||
z-index: 2;
|
||
}
|
||
|
||
[data-chart="burndown"] tr {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: flex-end;
|
||
flex: 1;
|
||
block-size: 100%;
|
||
gap: var(--size-1);
|
||
}
|
||
|
||
/* Remaining-work bar — red, with ideal line overlay above it */
|
||
[data-chart="burndown"] td {
|
||
display: block;
|
||
inline-size: 100%;
|
||
block-size: calc(var(--chart-height) * var(--size, 0.5));
|
||
background: color-mix(in oklch, var(--chart-color-4, var(--accent-red, #e03131)), transparent 25%);
|
||
border-radius: var(--chart-radius) var(--chart-radius) 0 0;
|
||
order: 1;
|
||
padding: 0;
|
||
border: none;
|
||
color: transparent;
|
||
position: relative;
|
||
z-index: 1;
|
||
transition: opacity var(--ease);
|
||
}
|
||
|
||
[data-chart="burndown"] td:hover { opacity: 0.85; }
|
||
|
||
[data-chart="burndown"] th[scope="row"] {
|
||
font-size: var(--chart-label-size);
|
||
font-weight: 400;
|
||
color: var(--chart-label);
|
||
text-align: center;
|
||
order: 2;
|
||
padding-block-start: var(--size-1);
|
||
white-space: nowrap;
|
||
padding: 0;
|
||
margin-block-start: var(--size-2);
|
||
}
|
||
|
||
/* ── Spacing modifiers for area and line (port from bar/column) ──── */
|
||
|
||
[data-chart="area"][data-chart-spacing="1"] tbody { gap: 0; }
|
||
[data-chart="area"][data-chart-spacing="2"] tbody { gap: 2px; }
|
||
[data-chart="area"][data-chart-spacing="3"] tbody { gap: 6px; }
|
||
[data-chart="area"][data-chart-spacing="4"] tbody { gap: 12px; }
|
||
[data-chart="area"][data-chart-spacing="5"] tbody { gap: 20px; }
|
||
|
||
[data-chart="line"][data-chart-spacing="1"] tbody { gap: 0; }
|
||
[data-chart="line"][data-chart-spacing="2"] tbody { gap: 2px; }
|
||
[data-chart="line"][data-chart-spacing="3"] tbody { gap: 6px; }
|
||
[data-chart="line"][data-chart-spacing="4"] tbody { gap: 12px; }
|
||
[data-chart="line"][data-chart-spacing="5"] tbody { gap: 20px; }
|
||
|
||
/* ── data-chart-reverse modifier ────────────────────────────────── */
|
||
|
||
[data-chart="bar"][data-chart-reverse] tbody {
|
||
flex-direction: column-reverse;
|
||
}
|
||
|
||
[data-chart="column"][data-chart-reverse] tbody {
|
||
flex-direction: row-reverse;
|
||
}
|
||
|
||
/* ── data-chart-stacked on column ───────────────────────────────── */
|
||
|
||
[data-chart="column"][data-chart-stacked] tr {
|
||
flex-direction: row;
|
||
align-items: flex-end;
|
||
gap: 0;
|
||
}
|
||
|
||
[data-chart="column"][data-chart-stacked] td {
|
||
flex: 1;
|
||
border-radius: 0;
|
||
block-size: calc(var(--chart-height) * var(--size, 0.2));
|
||
}
|
||
|
||
[data-chart="column"][data-chart-stacked] td:first-of-type {
|
||
border-radius: var(--chart-radius) 0 0 0;
|
||
}
|
||
|
||
[data-chart="column"][data-chart-stacked] td:last-of-type {
|
||
border-radius: 0 var(--chart-radius) 0 0;
|
||
}
|
||
|
||
/* ── data-chart-labels on bar ────────────────────────────────────── */
|
||
|
||
[data-chart="bar"][data-chart-labels] thead {
|
||
display: block;
|
||
margin-block-end: var(--size-2);
|
||
}
|
||
|
||
[data-chart="bar"][data-chart-labels] thead th {
|
||
font-size: var(--chart-label-size);
|
||
color: var(--chart-label);
|
||
font-weight: 400;
|
||
}
|
||
|
||
} /* end @layer charts */
|