分析画面は、数字が正しいだけでは使い続けられません。暗い部屋では白い画面がまぶしく、スマホでは4列のカードが窮屈で、毎回同じ設定をし直すのは面倒です。
今回は、企業分析画面の見た目と操作を整える部分を読みます。テーマの切り替え、画面幅に応じた並び替え、グラフの描き直し、表示設定の保存の4つです。最後に、自分用のテーマを1つ追加します。
画面の企業と数値は架空のサンプルです。 実企業のデータへの切り替えは次回の実践⑧で扱います。
私たち株式会社Crabgrassは、企業の財務データなどをAPIで提供するZENITH Codeを開発しています。本記事は自社サービスのデータを使うためのテンプレートを題材にした実装例です。
この記事のコードは、公開サンプル集のcompany-analysisテンプレート(執筆時点の版)に対応しています。起動方法は実践④の「ローカルで開く準備」と同じです。前回の実践⑥では、決算期の選択と前期比を読みました。
1. 今回扱う画面と操作
| 操作 | 画面で変わるもの | 今回読むコード |
|---|---|---|
| 右上の「表示をカスタマイズ」を押す | テーマと表示項目の設定画面 |
index.html の #preferences
|
| テーマを「ダーク」にする | 背景・文字・グラフの色 |
setTheme() と style.css の色の変数 |
| ブラウザの幅を狭くする | カードの列数、グラフの並び |
style.css の @media
|
| 設定を変えて再読み込みする | 前回の設定のまま開く |
localStorage への保存と読み込み |
2. テーマは色の変数を入れ替える
テーマを切り替える処理は、body に印を付けるだけです。
function setTheme(theme) {
document.body.dataset.theme = theme;
document
.querySelectorAll("[data-theme-choice]")
.forEach((button) =>
button.setAttribute(
"aria-pressed",
String(button.dataset.themeChoice === theme),
),
);
// Layout widths change between themes; redraw using the existing snapshot.
if (current) {
chart("barChart", $("metric").value);
chart("marginChart", "operatingMargin", true);
}
}
document.body.dataset.theme = "dark" とすると、HTMLの <body> に data-theme="dark" が付きます。色を変える仕事は、CSSの側にあります。次は style.css の抜粋です。
:root {
color-scheme: light;
--navy: #0b1326;
--ink: #17263e;
--muted: #617187;
--line: #e1e8ef;
--accent: #087d98;
--surface: #f4f7fa;
}
body[data-theme="dark"] {
color-scheme: dark;
--ink: #f1f5fa;
--muted: #b7c1cf;
--accent: #c4f36b;
--surface: #0e131a;
--line: #39475a;
}
--ink や --accent は、CSSの変数(カスタムプロパティ)です。画面のあちこちでは color: var(--ink) のように変数を使っているので、テーマごとに変数の値を入れ替えるだけで、全体の色が変わります。部品ごとにダーク用の色を書き足す必要はありません。
ダークテーマは、明るいテーマの色を反転して作っているわけではありません。style.css のコメントにあるとおり、数値のラベルや出典の文字が、どのカードの上でも読めるように色を選び直しています。color-scheme: dark は、スクロールバーやプルダウンなど、ブラウザが描く部品も暗い色にする指定です。
グラフも描き直す
setTheme() の最後で、グラフを描き直しています。グラフはSVGで、chart() が描くときの枠の幅から棒の位置を計算しています。
const w = Math.max(300, Math.min(line ? 410 : 580, box.clientWidth - 32)),
テーマによってカードの枠線や余白が違い、グラフを置く枠の幅も少し変わります。そのため、色を変えたあとに今の幅で描き直します。データを取り直すわけではなく、すでにある current を使います。
3. 画面幅に応じて並びを変える
style.css には、画面幅で並びを変える @media が3段階あります。
| 画面幅 | 主な変化 |
|---|---|
| 1150px以下 | 左のメニューを細くし、余白と数値カードの文字を少し小さくする |
| 900px以下 | 左のメニューを隠し、2つのグラフを同じ幅で並べる(広い画面では1.5対1) |
| 620px以下 | カードを2列、グラフを1列にし、余白を詰める |
620px以下の設定の一部です。
@media (max-width: 620px) {
.kpis {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.charts {
grid-template-columns: 1fr;
}
}
minmax(0, 1fr) の 0 は、中の数字が長くても列の幅を押し広げないための指定です。1fr だけだと、桁の多い金額が入ったカードの列が広がり、2列の幅がそろわなくなることがあります。
グラフは画面幅が変わるたびに描き直す
CSSで枠の幅が変わっても、SVGの中の棒の位置は自動では変わりません。そこで、ブラウザの幅が変わったときにもグラフを描き直しています。
window.addEventListener("resize", () => {
if (current) {
chart("barChart", $("metric").value);
chart("marginChart", "operatingMargin", true);
}
});
画面全体を縮小表示するのではなく、その幅に合わせてグラフを作り直します。スマホでも目盛りの文字が小さくなりすぎないのはこのためです。
財務表は表の中だけ横に動かす
3期分の金額を円で並べる財務表は、スマホの幅に収まりません。表を .table-scroll で囲み、その中だけを横にスクロールできるようにしています。
.table-scroll {
overflow-x: auto;
}
table {
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
white-space: nowrap で金額が途中で折り返されないようにし、font-variant-numeric: tabular-nums で数字の幅をそろえています。数字の幅がそろうと、縦に並んだ金額の桁が一致して、大小を比べやすくなります。
620px以下では、表の1列目(項目名)を position: sticky で左に固定しています。横にスクロールしても、どの行が売上高かを見失いません。
スマホの入力欄は16px
.field select,.field input{font-size:16px}
iPhoneのSafariは、文字が16pxより小さい入力欄を選ぶと、画面を自動で拡大します。証券コードを入れるたびに拡大されると、そのあとの操作がしにくくなるので、スマホ幅の入力欄だけ16pxにしています。
4. 表示設定を保存する
テーマとチェックの状態は、ブラウザの localStorage に保存します。
const save=()=>{try{localStorage.setItem("zenith-research-display",JSON.stringify({theme:document.body.dataset.theme,comparison:$("showComparison").checked,help:$("showHelp").checked,compact:$("compactView").checked}));}catch{/* Storage may be unavailable in private mode. */}};
保存するのは、テーマと3つのチェックの状態だけです。企業のデータ、選んだ企業、APIキーは保存しません。設定画面の下にも「表示設定だけをこのブラウザに保存します。企業データやAPIキーは保存しません。」と書いています。
try で囲んでいるのは、ブラウザのプライベートモードなどで localStorage が使えないことがあるためです。保存できなくても、画面はそのまま使えます。
読み込むときは、保存された値をそのまま信じずに確かめます。
try{const pref=JSON.parse(localStorage.getItem("zenith-research-display")||"null");if(pref){if(["cool","pop","dark"].includes(pref.theme))setTheme(pref.theme);for(const [id,key]of [["showComparison","comparison"],["showHelp","help"],["compactView","compact"]])if(typeof pref[key]==="boolean")$(id).checked=pref[key];}}catch{/* Use defaults when saved preferences are invalid. */}
テーマは3つの名前のどれかのときだけ使い、チェックは true か false のときだけ反映します。古い版で保存した設定や、壊れた値が残っていても、既定の表示で開けるようにするためです。
5. コンパクト表示
「コンパクト表示」にチェックを入れると、body に compact が付き、余白とグラフの高さを詰めます。
.compact .kpi{padding:14px 18px}
.compact .chart svg{max-height:175px}
.compact th,.compact td{padding-top:8px;padding-bottom:8px}
文字の大きさは変えていません。ノートPCの小さな画面でも、スクロールせずに数値カードとグラフを一度に見られるようにするための設定です。
6. 自分用のテーマを1つ追加する
ここまでの仕組みを使って、4つ目のテーマ「ウォーム」を追加します。変更する場所は4つです。
1. ボタンを足す(index.html)。ダークのボタンの後ろに足します。
<button type="button" data-theme-choice="warm" aria-pressed="false">
<span class="swatch warm" aria-hidden="true"></span
><span data-i18n="warm"></span
><small data-i18n="warmNote"></small>
</button>
2. 色の変数を足す(style.css の末尾)。
body[data-theme="warm"] {
--navy: #3a2a1f;
--ink: #2f241c;
--muted: #7a6656;
--accent: #b5562b;
--bar-soft: #f1c7a8;
--surface: #fbf6f0;
--line: #eadccd;
}
.swatch.warm {
background: linear-gradient(135deg, #3a2a1f 50%, #b5562b 50%);
}
3. ボタンの文字を足す(app.mjs の texts)。ja と en の両方に足します。
warm: "ウォーム",
warmNote: "紙の資料に近い色",
warm: "Warm",
warmNote: "Close to printed reports",
4. 保存した設定の確認に名前を足す(app.mjs)。
if(["cool","pop","dark","warm"].includes(pref.theme))setTheme(pref.theme);
4つ目を忘れると、ウォームを選んだあとに再読み込みしたとき、クールに戻ってしまいます。保存はできているのに、読み込むときの確認で弾かれるためです。
保存してブラウザを再読み込みし、「表示をカスタマイズ」でウォームを選びます。背景と文字、グラフの色が変わり、再読み込みしてもウォームのまま開けば完成です。
直近終値のカードは、どのテーマでも目立つように別の色を指定しています。ウォームでも色を変えたい場合は、style.css で .kpi.price の指定を探してください。
7. 確認箇所
| 症状 | 確認箇所 |
|---|---|
| テーマを変えても一部の色が変わらない | その部品が var(--ink) などの変数ではなく、色を直接書いていないか |
| 再読み込みするとテーマが戻る | 読み込み時の確認の配列に名前を足したか |
| 幅を変えるとグラフがはみ出す |
resize で描き直しているか |
| スマホで入力欄を選ぶと拡大される | 入力欄の文字が16px以上か |
| 表の金額が途中で折り返される |
white-space: nowrap が効いているか |
| 設定が保存されない | プライベートモードではないか。保存できなくても画面は動く仕様 |
見た目の変更をAIに頼む場合も、データや計算に触れないことを明記すると、確認する範囲を絞れます。
company-analysisテンプレートに、4つ目のテーマ「warm」を追加してください。
index.html のテーマのボタン、style.css の body[data-theme="warm"] の色の変数、
app.mjs の texts(ja と en)、保存した設定を読み込むときのテーマ名の確認、の4か所を変更します。
既存の3つのテーマの色は変えないでください。
グラフの計算、JSON、APIの呼び出しは変更しないでください。
文字と背景の色の差が十分あるかも確認し、変更箇所と確認する操作を説明してください。
8. 次回は実データにつなぐ
ここまでの4回は、架空の2社で画面の仕組みを読んできました。次回のZENITH Code実践⑧では、ZENITH CodeのAPIキーを使って、実在する企業のデータをこの画面に表示します。APIキーをブラウザに渡さない仕組みや、取得に失敗したときの表示も読みます。


