コンテンツにスキップ

チャート

チャートレシピは、サーバレンダリングされたデータテーブルObservable Plot の SVG チャートに 変えます。テーブルはデータソースであり、非 JavaScript のフォールバック であり、スクリーンリーダー用のデータです — installChart() がそれを 読んでチャートを描きます。チャートごとの JavaScript はありません: チャートの種類と系列ごとのマークはマークアップで宣言します。

このレシピはビヘイビアを必要としますinstallChart — そして Observable Plot、自分で読み込む任意のピア依存です。Plot は 決して @hypermedia-components/core にバンドルされないため、 installChart は自動初期化の behaviors エントリには含まれません。 Plot がなければビヘイビアは no-op で、テーブルが見えたままになります。

別名: グラフ、可視化。

リージョンを切り替えてみてください — 各ボタンが <figure class="hc-chart"> フラグメント(セマンティックなデータ テーブル)を api/recipes/chart/ 配下の実エンドポイントから再取得し、 installCharthtmx:load で再描画します。JavaScript がなければ 同じエンドポイントがテーブルを読めるページとして返します — テーブルこそがフォールバックそのものです。

Monthly sales
MonthSales
Jan120
Feb200
Mar150

Plot(ここでは CDN の UMD グローバル)を読み込み、ページごとに一度 インストールします:

<script src="https://cdn.jsdelivr.net/npm/@observablehq/plot@0.6/dist/plot.umd.min.js"></script>
<script type="module">
import { installChart } from '@hypermedia-components/core';
installChart(document, { plot: window.Plot });
</script>

installChartwindow.Plot を自動で拾うため、CDN スクリプトの実行 後なら installChart() 単体でも動きます。plot を明示的に渡すほうが 明快で、グローバルの代わりにバンドルされたインポートも使えます。

カテゴリカルなチャートでは、テーブルはこう読まれます:

  • 1 列目 — x のカテゴリ。
  • 2〜N 列目 — 各列が 1 系列。<thead> のセルが系列名です。
  • <td> は数値に変換されます(1,2001200。通貨記号や % は 取り除かれます)。
  • 任意の <caption> はチャートのタイトルになります。

figure に data-hc-chart を設定します:

描画
bar縦棒。複数系列は積み上げ
lineノードの点つきの折れ線。系列ごとに 1 本。
area線の縁つきの塗りつぶし面。系列ごとに 1 つ。
combo列ごとのマーク — <th data-mark="bar|line|area"> を設定。

data-hc-chart は、自身の data-mark を持たない列のデフォルト マークです。bar / line / area は、すべての列が 1 つのマークを 共有する場合にすぎません。

Weekly sessions
WeekWebMobile
W14218
W24824
W33931
W45536
W56133
Storage used
MonthUsed
Jan120
Feb135
Mar170
Apr165
May210

data-mark で列ごとにマークを宣言します。combo の下で無印の列の デフォルトは bar です。

Monthly sales vs target
MonthSalesTarget
Jan120150
Feb200160
Mar150170

Plot は線を棒のバンドスケールに自動で載せるため、点は棒の中心に揃い ます。系列が y の範囲を共有していれば追加の設定は不要です。本物の 第 2 y 軸はこのレシピのスコープ外です。

積み上げ、グループ、横棒、散布図、スパークライン

Section titled “積み上げ、グループ、横棒、散布図、スパークライン”

同じテーブル契約を、figure 全体の 6 つのプリセットが拡張します (列ごとの data-mark コンボはこれらには適用されません)。

複数系列が積み上がります(複数系列の bar がすでにやることの 明示形)。

Orders by channel
QuarterStoreOnline
Q18045
Q29570
Q36098

系列が各カテゴリ内で横並びに描画されます。カテゴリ軸がラベルを運び、 凡例が系列名を示します。

Orders by channel
QuarterStoreOnline
Q18045
Q29570
Q36098

ランキングの形です: カテゴリが y に載るため長いラベルが読みやすく、 値は x に載ります。複数系列は積み上がります。data-y-labeldata-y-min / data-y-maxdata-y-format値軸(ここでは x)を 設定し、値なしの data-tip はカテゴリ軸に沿ってスナップします。

Top products
ProductSales
Wireless headphones320
Mechanical keyboard260
USB-C dock210
Laptop stand150
Webcam90

各カテゴリの中で系列が横方向に並びます(行軸でファセット)。

Orders by channel
QuarterStoreOnline
Q18045
Q29570
Q36098

数値軸 2 本: 1 列目が数値の x(data-x-type のデフォルトは number)、各系列列が 1 つの点集合、任意の <th data-role="r"> 列が 点の半径を駆動します。

Height vs weight
HeightWeightCount
150523
158586
1656312
172709
180784

コンパクトな Plot スタイルのトレンド: 軸・グリッド・凡例はオフ、 data-height の指定がなければ高さ 48 px。依存なしのインライン トレンドには、単体の スパークラインコンポーネントの ほうがたいてい適しています — このプリセットは Plot で統一された ダッシュボードのためにあります。

30-day trend
DayValue
112
218
39
422
517
628
725

1 本の数値列をカウントの棒にビニングします(data-x-type の デフォルトは number。余分な列は無視。data-bins がビン数の上限)。

Response times (ms)
ms
90
95
110
120
125
140
160
180
210
260
340
520

行カテゴリが y、列ヘッダーが x、セル値が連続の塗りをカラー凡例 つきで駆動するマトリクス形(data-scheme が Plot のスキームを選び ます。カテゴリカルな系列パレットは適用されません)。行 / 列の順序は 書かれたとおりに保たれます。

Visits by slot
SlotMonTueWedThuFri
Morning37584
Afternoon64957
Evening926310

財務ブリッジ図です: 符号付きの増減を 1 列だけ読み、直前までの 累計から増減後の累計まで棒が浮いて描かれます。<tr data-total> を 付けた行は絶対値のアンカーです — セルには実際の合計値を入れます (非 JavaScript のテーブルが正しいままになります)。その棒は 0 から その値まで描かれ、累計はその値にリセットされます。増加 / 減少 / 合計の 棒はそれぞれ --hc-chart-waterfall-increase / -decrease / -total で着色されます(デフォルトは success / error / muted)。

Cash bridge
StepAmount
Opening100
Sales+80
Refunds-15
Costs-30
Closing135

data-tip を付けると、ツールチップにステップ・累計・増減が表示され ます。凡例(increase / decrease / total)はデフォルトで表示され、 data-legend="false" で非表示にできます。

data-tip を付けると、ホバーでツールチップが表示されます — x 値、 y 値、そして(複数系列なら)系列名。コンボチャートでも同時に表示される ツールチップは常に 1 つだけです。値なしの data-tip は x 方向に スナップします(棒・折れ線・時系列に最適)。scatter は最近傍点 (xy)がデフォルトです。data-tip="x" / "y" / "xy" で明示指定 できます。histogram のツールチップはビンの範囲と件数を、heatmap は 行 × 列 + 値を表示します。デモをホバーしてみてください:

Weekly sessions
WeekWebMobile
W14218
W24824
W33931
W45536
W56133

ツールチップはクライアント側のインタラクションです — サーバサイドレンダリングのパスには 適用されません。

1列目のセルを本物のリンクにして figure に data-link を付けると、 棒・点・セルのクリックがその行のアンカーへ転送されます。チャートは URL もリクエストも一切持ちません: 素の href ならページ遷移、 アンカーに htmx 属性(data-hx-getdata-hx-targetdata-hx-push-url など)があれば書いたとおりに swap されます。同じ リンクが非 JavaScript・キーボード・スクリーンリーダーの操作経路にも なります(テーブルがアクセシブルな操作面であり続けます)。

Sales by product (click a bar)
ProductSales
Alpha320
Beta180
Gamma240

アンカーのない行は単に遷移しません。フォーカス追跡はツールチップの pointer を再利用します(data-tip なしの場合は不可視のプローブ)。 pointer の半径 40px を超えた余白のクリックは何もせず、カーソルは クリック可能なデータにフォーカスしている間だけポインターになります。 クライアント側限定で、histogram は対象外です(ビンが複数行を集約 するため)。

figure 共通のフォームモード(カテゴリ × 系列)

Section titled “figure 共通のフォームモード(カテゴリ × 系列)”

行アンカーの粒度は単位です。「クリックの意味はサーバが決める」 形にしたい場合や、複数系列チャートでどの系列がクリックされたかまで 必要な場合は、figure の中に <form> を置きます。クリック時に、データの キー(xseriesvalue など)と一致する name を持つフィールドが フォーカス中のデータで埋められ、フォームが送信されます — リクエストは フォーム上の htmx が所有します:

<figure class="hc-chart" data-hc-chart="bar-grouped" data-link data-tip>
<form data-hx-get="/reports/drill" data-hx-target="#detail">
<input type="hidden" name="x">
<input type="hidden" name="series">
</form>
<table class="hc-table"></table>
</figure>
<!-- Feb × Osaka をクリック → GET /reports/drill?x=Feb&series=Osaka -->

<form> は行アンカーより優先され、すべてのデータがクリック可能に なります。非 JavaScript でもドリルダウンできる経路が必要なら、hidden の 代わりに可視のフィールド(<select name="x"> + 送信ボタン)にして ください — 同じエンドポイントが両方に応えます。

data-y-min / data-y-max は y のドメインを固定します — パーセントを 0–100 に固定する、ダッシュボード内の複数チャートで軸を 揃える、折れ線をゼロ始まり以外にする、といった用途です(0 がドメイン外 になるとゼロ基準線は自動で省かれます)。data-y-formatd3-format 文字列で目盛りを整形します ("s"1.2k".0%"",.0f")。

Monthly revenue
MonthRevenue
Jan4,200
Feb4,650
Mar4,400
Apr5,100
May5,600

積み上げ棒では両端を明示的に指定してください — フォールバックの データ範囲は生のセル値を読むため、積み上げを考慮しません。

属性デフォルト効果
data-y-label(なし)y 軸ラベル。
data-title<caption>チャートタイトル。
data-x-typecategorycategory | number | date
data-widthコンテナ幅Plot の幅(px)。
data-height--hc-chart-height(320px)Plot の高さ(px)。
data-legend自動(系列 ≥2 でオン)false でカラー凡例を非表示。
data-tipオフホバーツールチップ。値なし、または x | y | xy
data-y-min / data-y-maxデータ範囲y ドメインを固定。
data-y-formatPlot 既定y 目盛りの表示形式(d3-format 文字列)。
data-linkオフクリックを行の1列目アンカーへ転送。

棒は category の x を期待します。number / date の x は line / area に向きます。

それ以外はすべて: buildOptions フック

Section titled “それ以外はすべて: buildOptions フック”

属性は高頻度のニーズをカバーします。それ以外は installChartbuildOptions を渡してください — 描画直前に最終的な Plot スペックと figure を受け取り、返したものがそのまま Plot に描画されます:

installChart(document, {
plot: window.Plot,
buildOptions: (spec, figure) => ({
...spec,
marginLeft: 60,
y: { ...spec.y, ticks: 4 },
}),
});

何も返さなければ構築済みのスペックが使われるため、フックは観察だけに 使うこともできます。

チャートのクローム(目盛り、軸ラベル、グリッド)は --hc-chart-axis--hc-chart-grid を通じてテーマに従います。 系列色は固定のカテゴリカルパレット --hc-chart-series-1--hc-chart-series-6 から来ます。描画時に解決 され、Plot のカラースケールへ渡されます。パレットは意図的に テーマ非依存です — チャートの系列はブランドのアクセントに追従する より、相互に区別可能であり続けるべきだからです。

:root {
--hc-chart-series-1: #2563eb; /* override per series */
}

installCharthtmx:load を待ち受けるため、ページへスワップされた チャートは自動で描画されます — スワップごとの JavaScript は不要です。

<div data-hx-get="/reports/sales" data-hx-trigger="load" data-hx-swap="innerHTML">
<!-- The server returns the <figure class="hc-chart">…</figure> fragment. -->
</div>

非 htmx リクエスト(フルページロード)にも同じ読めるテーブルを返し、 非 JavaScript パスを機能させてください。フラグメントをレイアウトで包む 場合は HX-Request: true ヘッダーで htmx を検知します。

  • ソーステーブルは保持されます — 取り除かれるのではなく .hc-sr-only でアクセシビリティツリーへ移されるため、支援技術は 完全なデータを読めます。
  • 描画された <svg>aria-hidden="true" です: テーブルの装飾的な 複製なので、二重に読み上げられません。
  • テーブルには常にチャートを説明する <caption> を与えてください。

プログレッシブエンハンスメント

Section titled “プログレッシブエンハンスメント”
  • JavaScript なし<table class="hc-table"> が普通の読める テーブルとして描画されます。
  • JavaScript あり、Plot なし → 同じ: Plot なしの installChart は no-op です。
  • JavaScript + Plot → テーブルはアクセシビリティツリーへ移り、 SVG チャートが表示されます。

チャートは、DOM シム(linkedom)の 下の Plot でサーバ上でも SVG に描画し、インラインで返せます — クライアント側の Plot は不要になります。その場合は明示的な marginLeft / marginBottom を設定してください — サーバの DOM シムは 自動軸マージンのためのテキスト計測をしません。figure は data-state="rendered"(+ hc-sr-only のテーブルと aria-hidden の SVG)つきで出力します: installChart() は描画済みの figure を認識して 放っておくため、SSR とクライアント描画のチャートは共存します。 トークンはクライアント側 CSS です — SSR では明示的な色を渡して ください。動くスニペットは 契約に あります。

  • テーブル — チャートが 読み取るセマンティックなテーブル。
  • データ領域 — イベントに応じて領域(とそのチャート)を更新。