Enarratio
可访问的,服务器渲染的图表和科学数字的网络。
现状:正在积极开发中。 Enarratio是一个。API可能会改变 在1.0之前,每个更改都记录在 更新日志。
Enarratio在服务器上绘制图表到HTML中。一张图表之前是完整的 任何JavaScript运行:阅读器立即看到它,屏幕阅读器得到一个写入 描述和等效的数据表、搜索引擎和语言模型 人们读同样的数字,这个页面不下载图表库。 一个可选的增强层可以添加悬停细节、键盘导航、 点击过滤事件和范围刷在顶部。
它涵盖了网站所需的图表(条形图、线条图、区域图、散点图、 热图,网络,带有预测带和时间轴),小原语a 仪表板需要(火花线,进度环,正常运行时间和热条,事件 时间线)和 实验室需要的数据,从抗体滴度图和基因组开始 痕迹。图表是建立在 可观察到的情节 和 d3-force (可选的对等依赖,已安装 (仅适用于使用它们的图表),并返回纯HTML字符串,因此 Enarratio适用于任何服务器框架。它在Node.js 24和26上进行了测试 并在Cloudflare Workers上运行(该画廊的第一个网站以一种方式呈现图表) 工人);Deno和Bun应该可以工作,因为它只使用标准模块 尚未测试。
画廊: 每个图表都有两个主题,光明和黑暗:
enarratio.dustinedwards.info
(由 npm run gallery).
天文学和EmDash: enarratio-astro 在Astro页面和Markdown中绘制Enarratio图表,以及 enarratio-emdash 将它们添加到 将EmDash编辑器作为图表块。它们是单独的包 DrDustinEdwards /阿斯特罗;看到 enarratio.dustinedwards.info /阿斯特罗。
需要说明
大多数web图表库都是在浏览器中绘制的。在浏览器中绘制的图表 在它的脚本下载和运行之前是不存在的,对任何东西都是不可见的 在不运行脚本的情况下读取HTML,并且通常对屏幕阅读器不透明。 存在静态图像的服务器端替代方案,但图像无法遵循 该网站的暗模式无法被辅助技术读取 文本,不能进行交互。
网络上的科学数据也有同样的问题,而且还有第二个问题 研究人员发表的数据(滴度图、基因组图、剂量反应曲线、 系统发育,监测图)不在通用的图表库中, 所以每个实验室都是手工绘制的,不一致,而且通常没有 文本的选择。
Enarratio适用于研究、教学和小型组织的开发人员 想要图表的网站:
- 在服务器上呈现 作为语义HTML中的内联SVG,没有客户机 所需的代码;
- 可通过建筑进入:书面描述(生成自。 (当没有给出时的数据),一个等效的数据表,键盘访问和 屏幕阅读器的每一个互动公告,不依赖于颜色 独自一人;
- 遵循网站的主题 在光明和黑暗模式下,从一个渲染,与 调色板检查对比度和色觉缺陷;
- 包括科学图表类型 与一般的设计一起设计, 分享它们的主题、可访问性和交互性。
安装
npm install enarratio
这就是核心:主题、样式表、颜色检查和小字体 原语(火花线、进度环、正常运行时间条等),没有 运行时依赖关系。图表是单独的入口点,每个入口点都有 它需要的包作为可选的对等依赖项,因此站点只安装 它吸引了:
| 进口 | 持有 | 也安装 |
|---|---|---|
enarratio | 核心、原语和 tableChart | 没有什么 |
enarratio/plot | lineChart, areaChart, barChart, scatterPlot, heatmap 下面的图表 | @observablehq/plot linkedom |
enarratio/science | genomeTrack, titerPlot, doseResponse, survivalPlot, forestPlot, phylogeneticTree, networkChart,以及他们的统计数据 | @observablehq/plot d3-force linkedom |
enarratio/map | dotMap 和 choroplethMap,从您自己的GeoJSON中绘制 | 没有什么 |
enarratio/lite | barChart, lineChart, areaChart 没有Observable Plot,用于浏览器;也 enarratio/lite/bar, /line, /area | 没有什么 |
enarratio/enhance | 浏览器交互 | 没有什么 |
enarratio/redraw | 重新绘制服务器的图表到它的帧宽度; enarratio/redraw/narrow 持有 narrowChart,仅交换到服务器的狭窄绘图 | 没有什么 |
enarratio/themes | defaultTheme | 没有什么 |
enarratio/base.css | 无主题样式表 | 没有什么 |
(linkedom 是从浏览器包中删除的,它使用浏览器自己的
文档)。要在浏览器中绘制条形图、折线图和面积图,请从
enarratio/lite:提供相同的选项(包括错误栏和注释)
相同的SVG,没有可观察图,链接或DOM,每个图表15到16 kB
捆绑,缩小和压缩。Enarratio仅支持ESM,需要Node.js 22或
(或任何带有标准ES模块的运行时)。包括TypeScript类型。
五分钟开始
1. 在服务器上呈现图表。 每个图表都有它的数据,字段
情节, alt说明图表所显示内容的句子。
import { stylesheet } from "enarratio";
import { barChart } from "enarratio/plot";
import { defaultTheme } from "enarratio/themes";
const entries = [
{ year: 2022, type: "Publications" },
{ year: 2022, type: "Talks" },
{ year: 2023, type: "Publications" },
{ year: 2024, type: "Publications" },
{ year: 2024, type: "Grants" },
];
const chart = barChart({
id: "entries",
data: entries,
x: "year",
series: "type", // stacks by type; omit y to count rows
title: "Entries per year",
alt: "Entries per year by type, 2022 to 2024: one or two a year, mostly publications.",
});
2. 将其放入具有主题样式表的页面中。 样式表为 无论图表的数量多少,每页都包含一次。
const html = `<!doctype html>
<html lang="en">
<head>
<meta name="color-scheme" content="light dark">
<style>${stylesheet(defaultTheme)}</style>
</head>
<body>${chart}</body>
</html>`;
该页面完成了:图表、图例和数据表
脚本关闭,然后 color-scheme 元标签使它们跟随读者的
浅色或深色偏好。
3. 可选地,在浏览器中增强它。
import { enhance } from "enarratio/enhance";
enhance(); // every Enarratio chart on the page
document.addEventListener("enarratio:select", (event) => {
const { chartId, field, value } = event.detail; // e.g. "entries", "type", "Talks"
console.log(chartId, field, value); // value is null when the filter is cleared
});
现在条形图显示悬停和聚焦的细节,箭头键在它们之间移动
一个系列和系列之间),输入或点击过滤器按栏的系列
(和火灾 enarratio:select),图例条目变成切换按钮,拖动,
用方向键移动或从-到窗体选择时间轴上的范围
(enarratio:brush),数据表排序并可以替换图表。
逃避清除。所有的东西都被宣布给屏幕阅读器,动作停止
下 prefers-reduced-motion。 { expand: true, url: true } 添加扩展的
查看并保留地址中的过滤器; strings 翻译图层。
要在服务器上绘制适合手机的宽页面的图表,请在旁边写上它的规格
redrawAttribute 并调用 redrawCharts() (或 redrawChart (一个主人)从
enarratio/redraw 相反,每个图表都以其帧的宽度作为该宽度重新绘制
变化,然后增强。
没有捆绑者,发球 node_modules/enarratio/dist/enhance/index.js 作为一个
文件, sound.js 和 image.js 从它旁边的同一个文件夹(index.js
只有当读者按下“按声音播放”时,才会导入第一个,而第二个则按下
第一次下载),然后用
<script type="module">import { enhance } from "/enhance.js"; enhance();</script>,
或从CDN,如
https://cdn.jsdelivr.net/npm/[email protected]/dist/enhance/index.js。
要重新绘制图表,请将新的服务器标记传递给 update() 在图表上
enhance() 的回报。该标记应该来自Enarratio:它是经过清理的
但它不是插入任意HTML的一种方式。
这个 例子 目录中每个图表的可运行文件
画廊。每个导出图表的HTML,和 npm run gallery 使他们
所有成 site/dist。
图表
| 函数 | 进口 | 吸引了 |
|---|---|---|
barChart | enarratio/plot | 条,堆叠或分组,垂直或水平,在一个类别或时间轴上;在未给定值时计数行;误差线 |
divergingBarChart | enarratio/plot | 符号值大约为零,每个条都标有它的符号 |
waterfallChart | enarratio/plot | 一座从一个总数到另一个总数的桥梁,通过增加和减少 |
lineChart | enarratio/plot | 随时间变化的线或任何数字,带有间隙、参考线、事件标记、直接标签、log y,以及在选定的x后虚线的预测带 |
areaChart | enarratio/plot | 随着时间堆叠区域,或者堆叠到100% |
smallMultiples | enarratio/plot | 每组一个柱状图或折线图,在共享的尺度上 |
scatterPlot | enarratio/plot | 线性或对数轴上的点,每个序列的符号,95%波段的可选回归,带有大小图示的气泡,误差条 |
histogram, densityPlot | enarratio/plot | 分布:分类计数和核密度曲线 |
boxPlot, stripPlot | enarratio/plot | 箱形图,每一个值都是蜂群 |
dotPlot, rangePlot, dumbbellPlot | enarratio/plot | 每项一行:点(带间隔)、从低到高的范围和两个连接在一起的值 |
slopeChart | enarratio/plot | 每件物品两次的价值,在两端贴上标签 |
pieChart | enarratio/plot | 一种馅饼或甜甜圈,据报道超过六片就有问题 |
heatmap | enarratio/plot | 主题顺序坡道上的网格,带有打印值和标记的箱子 |
tableChart | enarratio | 一个可分类的表格与行内酒吧,热电池和火花线 |
networkChart | enarratio/science | 一个部署在服务器上的强制定向网络,是确定的 |
titerPlot | enarratio/science | 稀释轴上的滴度具有几何平均值、95% CI和检测限 |
genomeTrack | enarratio/science | 在核苷酸轴上缩放的特征,以链为单位的箭头,在通道中重叠 |
doseResponse | enarratio/science | 一个四参数logistic曲线,拟合或给定,有EC50标记 |
survivalPlot | enarratio/science | Kaplan-Meier曲线带有审查标记、置信带和处于风险中的数字 |
forestPlot | enarratio/science | 有间隔的研究估计,和混合估计(固定或随机效应) |
phylogeneticTree | enarratio/science | 纽威克的一棵树,矩形,顶端按组着色,结点处有支撑 |
dotMap | enarratio/map | 在你的街道或海岸线上按价值大小划分的点,地标作为正方形,一个突出显示 |
choroplethMap | enarratio/map | 在连续斜坡的五个标记步骤的区域,值打印在他们适合的地方,没有数据概述 |
sparkline | enarratio | 一个字大小的趋势线 |
progressRing | enarratio | 向总目标迈进 |
uptimeStrip | enarratio | 上升、下降、下降或未测量,每周期一个刻度 |
heatStrip | enarratio | 顺序斜坡上的单行条带,每个值一个单元格 |
timeline | enarratio | 站点或代理的通道,时间窗口上的跨度和点事件,颜色和形状的状态 |
预测是由你计算出来的,只由Enarratio绘制 lineChart 这个
界如田和界,它遮蔽了带,虚线之后
边界,绘制空心预测点,并在数据中标记预测行
表格。时间轴在 barChart 不需要预先格式化的标签:
import { barChart, lineChart } from "enarratio/plot";
lineChart({
data: days, // { day, visits, lower, upper }, the last rows computed by your model
x: "day",
y: "visits",
band: { lower: "lower", upper: "upper", label: "95% interval" },
forecastFrom: "2026-02-23",
alt: "Daily visits, with a one-week forecast and its 95% interval from 24 February.",
});
barChart({
data: runs, // { at: "2026-06-09T08:00Z", site }, one row per run
x: "at",
xType: "time", // one bar per day, quiet days included
series: "site",
alt: "Analysis runs per day at three sites.",
});
每个原语也可以表示它的数字: sparklineTable,
uptimeStripTable, progressRingTable, timelineTable 和 heatStripTable
返回一个表和一个总结句,这些表和摘要句是根据与
绘图,在需要的页面旁边。
文档中记录了每个函数 API参考 在它里面 打印稿声明。图表类型仍将出现,以及它们的顺序 进来吧,都在 科学海图。
主题
主题是数据:字体、网格线、明暗配色方案
有八个系列颜色,一个五步顺序坡道和状态颜色。
stylesheet(theme) 将其转换为CSS自定义属性;图表仅指
这些属性,所以一个服务器呈现服务于每个主题和两个方案。
import { checkTheme, defineTheme, stylesheet } from "enarratio";
import { defaultTheme, highContrastTheme } from "enarratio/themes";
const theme = defineTheme({
...defaultTheme,
name: "lab",
fonts: { body: "Inter, system-ui, sans-serif" },
light: { ...defaultTheme.light, background: "#fbfaf7" },
});
const report = checkTheme(theme);
// report.issues: text below 4.5:1, marks below 3:1 (WCAG 2.2),
// series names below 4.5:1 on the background, ramp steps under 3:1 by fill
// and edge, and series colors closer than 10 CIEDE2000 units under simulated
// protanopia, deuteranopia or tritanopia. stylesheet() throws on any of them.
const css = stylesheet(theme, { highContrast: highContrastTheme });
背景上写的系列名称(直接标签、生存曲线标签)
以达到4.5:1的每个系列颜色的阴影绘制,并且坡道步骤
太苍白,看不到背景(热图的最低值)得到的优势
3:1。 stylesheet() 既可以从主题中衍生,也可以采用主题本身
seriesLabels 和 sequentialEdges。
您自己的标记在一个主题
任何主题颜色都可以是网站自己的自定义属性之一,具有其值
作为后备: var(--brand-ink, #1c1727). 样式表按照给定的方式编写它,
因此,图表跟随标记,无论网站改变它(一个家庭,一个密度,一个
它自己的黑暗模式),并且没有任何东西被十六进制复制。 checkTheme() 措施
回退,所以保持它等于令牌的值。
const theme = defineTheme({
...defaultTheme,
name: "site",
light: {
...defaultTheme.light,
background: "var(--surface, #fdfdfe)",
text: "var(--text, #1c1727)",
mutedText: "var(--muted, #686471)",
},
});
当级数或斜坡是符号时,给出 seriesLabels 和
sequentialEdges 也是一种象征;否则它们的阴影是由
后退并保持固定。
颜色和类型已经存在于自己的设计标记中的站点可以跳过
Theme 对象:链接 enarratio/base.css (或称 baseStylesheet()),定义
颜色和字体属性自己,并调整任何大小,重量,半径或动画
举个例子,时间也是一样的 --enarratio-font-size-mark 或
--enarratio-duration-grow. 每个属性都有今天的值作为回退,所以
设置none不会改变任何东西。完整的列表在
API参考。
任何图表都可以用 colors: { Talks: "#8a4a1b" } 或者一个
浅色和深色的一对。图表在data属性中携带它,而不是 style
属性,因此严格的Content-Security-Policy不需要 'unsafe-inline':添加
colorStyles(html) 到页面的CSS。包中附带了两个主题
enarratio/themes: defaultTheme, highContrastTheme,这个例子
上面的用法适用于需要更多对比的读者。这个
画廊的第二页使用了一个中立的,大量评论的示例主题,
例子/主题/ example.ts,是为了复制而写的
和编辑。
可访问性
- 每个图表都是一个带有标题的图形,一个可见的一句话发现(图表)
alt),屏幕阅读器概述(类型,轴和范围,系列和点), 从数据(趋势、极值、异常值、间隔)、注释、源行、“下载CSV”链接和数据计算得出的摘要 表,所有这些都在HTML中,脚本关闭。原始人也是人物,除非inline。 alt将图表的SVG (role="img"),属于SVG 对图形进行修改,使标题和表格保持可读性。一张没有 一个是根据它的数据来命名自己,并报告一个问题;strict: true转 每一期(没有Alt或标题,没有标记的轴,太多的系列或点, (CSV太大而无法内联)导致生成失败的错误。- 数字、日期和每个生成的字符串紧随其后
locale和strings。 - 每个图表都带有一个等效的数据表,无论是公开的还是直观的 隐藏的。
- 每个标记都有悬停细节,通过SVG无需脚本即可工作
<title>。 - 系列的区别不仅仅在于色调:填充条和纹理 区域,图例和直接标签(分开,以免重叠), 散点图中的符号,探测极限以下的空心点,高度 在正常运行时间条中,虚线表示缺失值。
- 绘图中的文本遵循浏览器的字体大小,并且永远不会在 默认缩放为12px,缩小到320px。
- 图表遵循强制颜色(Windows高对比度),
prefers-contrast: more,prefers-reduced-motion和印刷,和携带没有style属性。 - 图表的绘制是一种名为图像(
role="img"),是否增强;这个 增强层的点在它旁边,在它们自己的role="application"层(创纪录的0027). - 增强的图表增加了“下载SVG”,“下载PNG”,“下载触觉SVG”
(黑底白底,11 * 11.5英寸的纸张,用于盲文压花或膨胀
纸)和带an的图形
id,“复制嵌入”(链接到图表) 旁边的“下载CSV”和“播放声音”,播放每一个系列的上升 还有降调。 - 地图不接受Enarratio的边界:您传递GeoJSON,每个地图都有一个 总结,一个数据表和一个键盘从北向南走,从来没有 仅通过颜色显示值。
- 增强的图表是键盘操作,一个选项卡停止,通过本机
按钮上的标记和描述的关键提示;他们宣布每个点
通过活动区域放置、过滤、范围、排序和更新,以及尊重
prefers-reduced-motion。 - 测试套件在画廊上运行了两个主题,光和 黑暗,有剧本或没有剧本。
发展
npm ci
npm run typecheck && npm run lint && npm test # unit and rendering tests
npm run build && npm run gallery # site/dist
npm run test:browser # accessibility, interaction, visual
开发需要Node.js 24或更高版本,它直接运行TypeScript脚本; .nvmrc
针26。
画廊在 https://enarratio.dustinedwards.info 仅作为资产
Cloudflare Worker (wrangler.jsonc),并有严格的内容安全策略
以及投资组合的共享安全标头,来自站点运行时站点注册表中的图库条目
(worker/index.ts, _headers 从 site-runtime-headers). npm run deploy:gallery 构建和
部署;“部署图库”工作流在每次推送时都做同样的事情
main 一旦存储库拥有 CLOUDFLARE_API_TOKEN 和
CLOUDFLARE_ACCOUNT_ID 的秘密。看到
文档/设计/ 0008 -画廊hosting.md. 看到
CONTRIBUTING.md 对于工作流和
文档/设计 埃纳拉比为什么会是现在这个样子
引用
如果您在研究中使用Enarratio,请使用元数据引用 CITATION.cff。
许可证
麻省理工学院。达斯汀·爱德华兹版权所有。