您可能知道一些使用纯 CSS 创建图表的方法。其中一些在 CSS-Tricks 上有介绍,还有许多其他方法可以在 CodePen 上找到,但我没有看到很多“面积图”的例子(想象一个底部区域填充的折线图),尤其是 HTML 和 CSS 结合使用的例子。在本文中,我们将使用语义和可访问的 HTML 基础来实现这一点。

让我们从 HTML 开始
为了简化操作,我们将使用 <ul>
标签作为包装器,并使用 <li>
元素来表示单个数据项。您可以在项目中使用任何其他 HTML 标签,具体取决于您的需求。
<ul class="area-chart">
<li> 40% </li>
<li> 80% </li>
<li> 60% </li>
<li> 100% </li>
<li> 30% </li>
</ul>
CSS 无法检索内部 HTML 文本,因此我们将使用 CSS 自定义属性将数据传递给我们的 CSS。每个数据项将具有 --start
和 --end
自定义属性。
<ul class="area-chart">
<li style="--start: 0.1; --end: 0.4;"> 40% </li>
<li style="--start: 0.4; --end: 0.8;"> 80% </li>
<li style="--start: 0.8; --end: 0.6;"> 60% </li>
<li style="--start: 0.6; --end: 1.0;"> 100% </li>
<li style="--start: 1.0; --end: 0.3;"> 30% </li>
</ul>
我们需要考虑以下几点…
在进入样式设计之前,我们应该考虑一些设计原则。
- 单位数据: 我们将在 HTML 中使用无单位数据(即没有
px
、em
、rem
、%
或任何其他单位)。--start
和--end
自定义属性将是 0 到 1 之间的数字。 - 列宽: 我们不会为每个
<li>
元素设置固定的width
。我们也不会使用%
,因为我们不知道有多少个项目。每列的宽度将基于主包装器宽度除以数据项总数。在本例中,即<ul>
元素的宽度除以<li>
元素的数量。 - 可访问性: 每个
<li>
中的值是可选的,只有--start
和--end
自定义属性是必需的。不过,最好包含某种文本或值,以便屏幕阅读器和其他辅助技术能够描述内容。
现在,让我们开始设计样式!
让我们先从一般布局样式开始。图表包装器元素是一个弹性容器,按行显示项目,并拉伸每个子元素,以填充整个区域。
.area-chart {
/* Reset */
margin: 0;
padding: 0;
border: 0;
/* Dimensions */
width: 100%;
max-width: var(--chart-width, 100%);
height: var(--chart-height, 300px);
/* Layout */
display: flex;
justify-content: stretch;
align-items: stretch;
flex-direction: row;
}
如果面积图包装器是一个列表,我们应该删除列表样式,以便获得更多样式灵活性。
ul.area-chart,
ol.area-chart {
list-style: none;
}
此代码对整个图表中的所有列进行样式设置。对于条形图,这很简单:我们使用 background-color
和 height
为每列设置样式。对于面积图,我们将使用 clip-path
属性来设置应该显示的区域。
首先,我们设置每列的样式
.area-chart > * {
/* Even size items */
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0;
/* Color */
background: var(--color, rgba(240, 50, 50, .75));
}
为了创建一个覆盖整个区域的矩形,我们将使用 clip-path
属性及其 polygon()
函数,该函数包含区域的坐标。目前,这实际上没有任何作用,因为多边形覆盖了所有内容。
.area-chart > * {
clip-path: polygon(
0% 0%, /* top left */
100% 0%, /* top right */
100% 100%, /* bottom right */
0% 100% /* bottom left */
);
}
现在,最棒的部分来了!
为了只显示列的一部分,我们将其剪切以创建类似面积图的效果。为了只显示我们想要的区域,我们在 clip-path
多边形中使用 --start
和 --end
自定义属性。
.area-chart > * {
clip-path: polygon(
0% calc(100% * (1 - var(--start))),
100% calc(100% * (1 - var(--end))),
100% 100%,
0% 100%
);
}
说真的,这行 CSS 代码完成了所有工作。以下是我们的成果。
使用多个数据集
现在我们已经了解了基础知识,让我们创建一个具有多个数据集的面积图。面积图通常测量多个数据集,其效果是对数据进行分层比较。

这种图表需要多个子元素,因此我们将用 <table>
替换我们的 <ul>
方法。
<table class="area-chart">
<tbody>
<tr>
<td> 40% </td>
<td> 80% </td>
</tr>
<tr>
<td> 60% </td>
<td> 100% </td>
</tr>
</tbody>
</table>
表格是可访问的,并且对搜索引擎友好。如果样式表由于某种原因未加载,所有数据仍然可见。
同样,我们将使用 --start
和 --end
自定义属性,其值介于 0 和 1 之间。
<table class="area-chart">
<tbody>
<tr>
<td style="--start: 0; --end: 0.4;"> 40% </td>
<td style="--start: 0; --end: 0.8;"> 80% </td>
</tr>
<tr>
<td style="--start: 0.4; --end: 0.6;"> 60% </td>
<td style="--start: 0.8; --end: 1.0;"> 100% </td>
</tr>
</tbody>
</table>
因此,首先我们将为包装元素(我们的表格)设置一般布局样式,我们已经为其添加了 .area-chart
类。
.area-chart {
/* Reset */
margin: 0;
padding: 0;
border: 0;
/* Dimensions */
width: 100%;
max-width: var(--chart-width, 600px);
height: var(--chart-height, 300px);
}
接下来,我们将使 <tbody>
元素成为弹性容器,按行显示 <tr>
项目,并使其大小一致。
.area-chart tbody {
width: 100%;
height: var(--chart-height, 300px);
/* Layout */
display: flex;
justify-content: stretch;
align-items: stretch;
flex-direction: row;
}
.area-chart tr {
/* Even size items */
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0;
}
现在,我们需要使 <td>
元素相互覆盖,每个元素位于另一个元素的顶部,这样我们就能够获得分层效果。每个 <td>
元素都覆盖了包含它的 <tr>
元素的整个区域。
.area-chart tr {
position: relative;
}
.area-chart td {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
让我们将 clip-path: polygon()
的神奇力量应用起来!我们只显示 --start
和 --end
自定义属性之间的区域,这些属性的值仍然是 0 到 1 之间的数字。
.area-chart td {
clip-path: polygon(
0% calc(100% * (1 - var(--start))),
100% calc(100% * (1 - var(--end))),
100% 100%,
0% 100%
);
}
现在,让我们为每个元素添加颜色。
.area-chart td {
background: var(--color);
}
.area-chart td:nth-of-type(1) {
--color: rgba(240, 50, 50, 0.75);
}
.area-chart td:nth-of-type(2) {
--color: rgba(255, 180, 50, 0.75);
}
.area-chart td:nth-of-type(3) {
--color: rgba(255, 220, 90, 0.75);
}
重要的是使用带有不透明度的颜色来获得更好的效果,这就是我们使用 rgba()
值的原因。如果您习惯使用 hsla()
,也可以在这里使用它。
就是这样!
总结
无论我们在图表中添加多少 HTML 元素,基于弹性的布局都能确保所有项目的大小一致。这样,我们只需要设置包装图表元素的宽度,项目就会相应调整以实现响应式布局。
我们已经介绍了一种使用纯 CSS 创建面积图的技术。对于高级用例,您可以查看我的新的开源数据可视化框架 ChartsCSS.org。查看 面积图 部分,了解如何使用不同的方向、轴,甚至反转顺序(无需更改 HTML 标记)等方式自定义面积图,还有更多功能!
在最终的 CodePen 中,图表的高度似乎在 Firefox(版本 83.0)中呈现得很奇怪。我在 Edge 或 Chrome 中查看时,它看起来很好。我进入 CodePen 并将图表 CSS 设置为
然后我就能看到最终的图表。
笔已修复。感谢 Caleb。
这真的太棒了!
第七个代码块中有一个小错误。
它写着
100% calc(100% * (1 - var(--size))),
,但可能应该是var(--end)
而不是var(--size)
。感谢您的这篇文章!
感谢您报告问题!我已经修复了代码块。
我真的很喜欢从常规表格中创建 CSS 图表的想法,如果样式表未加载或浏览器不支持必要的样式属性,它将回退到可用的数据视图。
是的,回退非常酷。但这种技术还有更多好处——语义结构、可访问性、搜索引擎可见性、无 JS 渲染等等…
有没有办法删除列表中每个项目之间的空格?